diff --git a/README.md b/README.md
index ab6a6c6fca..c03774e7fb 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ Java 8 SDK & NetBeans bundle: https://www.oracle.com/technetwork/pt/java/javase/
**Change log:**
- * Fixed Monster Magnet crashing the caster when trying to pull bosses.
+ * Fixed Monster Magnet crashing the caster when trying to pull bosses. Drawback: Dojo HPBar becomes unavailable.
* Fixed some 'rn' problems with quest icons & removed "tab" from party leader changed message. https://hostr.co/tsYsQzzV6xT0
diff --git a/docs/area_bosses/BossEvent.js b/docs/area_bosses/AreaBoss.js
similarity index 100%
rename from docs/area_bosses/BossEvent.js
rename to docs/area_bosses/AreaBoss.js
diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt
index 461deea159..c876cdebb7 100644
--- a/docs/mychanges_ptbr.txt
+++ b/docs/mychanges_ptbr.txt
@@ -2014,4 +2014,39 @@ Revisado limite de dano aplicável por alguns summons, cujo valor limite estava
Implementado normalização de fuso horário em pacotes enviados ao cliente. Agora o sistema utiliza mesmo fuso horário definido nas flags do servidor.
Corrigido certos casos onde grupos dentro de lobby de CPQ não conseguiam ser desafiados, geralmente ocorrendo ao se desconectar após o desafio ter sido aceito e antes de começar a instância.
Revisado script de créditos.
-Adicionado checagem por GM's no método de autoban de jogador.
\ No newline at end of file
+Adicionado checagem por GM's no método de autoban de jogador.
+
+17 Julho 2019,
+Corrigido drops de reatores não utilizando o sistema de drops sequenciais.
+Revisado uso de sincronizações em vários métodos do sistema, tais como nos métodos de colocação de novos itens no mapa, detecção de toque em reatores, tabela de convidados em casamento, aplicação de dano de jogadores em mobs, recepção de pacotes.
+
+18 Julho 2019,
+Corrigido aplicação indevida de requisição de palavra-chave que prosseguia quest em uma das quests na questline de Aran.
+Corrigido nome errado em coluna da tabela "reports".
+Corrigido uso de NPC default na conversa padrão que ocorre ao se utilizar o comando "startquest" e "completequest".
+
+19 Julho 2019,
+Corrigido quest onde mobs podem aparecer na área do NPC Grendel permitindo repetir os ganhos de quest tanto quanto respawn de mobs à vontade.
+Corrigido robes de sauna e outros, que permitem ganhos bônus de HP, gerando ganhos 10x maiores que o esperado.
+Ajustado limites para recuperação de HP de forma a permitir ganhos em vários casos onde há a aplicação de bônus, tais como usando sauna robe, Endure skill.
+
+22 Julho 2019,
+Corrigido atributo de contagem de dano em mob aliado da HenesysPQ não instanciado.
+Corrigido skill "Combat Step" sendo considerado um "buff" pelo sistema do servidor. Isso implicava em duplicação de efeito visual para outros jogadores.
+
+26 - 27 Julho 2019,
+Corrigido problemas de cast de tipos que passou a ocorrer após trocar para Java 8.
+Ajustado flag que permite jogadores a ganhar EXP de mob independente de diferenças de nível.
+Corrigido Gaviota não sumindo após lançar ataque.
+Corrigido funcionalidade de ignorar items de pets não se mantendo após trocar de mapas.
+Corrigido CPQ1 campo 3 e 4 não permitindo jogadores a usar summons/protectors em campo.
+Corrigido líderes de expedição recebendo pacote de timer para fase de registro em casos onde a expedição falhou em ser iniciada.
+Corrigido problema de locking ocorrendo recentemente ao tentar rodar limpeza de itens no mapa (ocorre ao realizar drops de vários itens, mais antigos imediatamente sumindo), problema ocorrendo devido a um caso de loop infinito.
+Corrigido várias skills de summons não utilizando o ícone de buff no canto superior direito da tela.
+Corrigido alguns danos de summons sendo calculados extremamente baixos quando o jogador não equipa uma arma ou o mesmo não possui pelo menos uma dezena em ataque.
+
+28 Julho 2019,
+Corrigido funcionalidade de loot explosivo de mobs não aplicando devidamente.
+Corrigido linguagens, bastante usado na MCPQ, não utilizando o valor requisitado pelo jogador ao logar/trocar de canais.
+Corrigido casos de NPE ao tentar realizar updates de posição lado-servidor em alguns summons de jogador.
+Revisado reset de reatores em reatores que estão desaparecidos por um tempo, para retornar de imediato.
\ No newline at end of file
diff --git a/launch.bat b/launch.bat
index 076a28d634..52b0eafefa 100644
--- a/launch.bat
+++ b/launch.bat
@@ -1,6 +1,6 @@
@echo off
@title HeavenMS
-set PATH=C:\Program Files\Java\jdk1.7.0_79\bin;%PATH%
+set PATH=C:\Program Files\Java\jdk1.8.0_211\bin;%PATH%
set CLASSPATH=.;dist\*
java -Xmx2048m -Dwzpath=wz\ net.server.Server
pause
\ No newline at end of file
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml
index 6fd0f0e472..d08448f6c7 100644
--- a/nbproject/build-impl.xml
+++ b/nbproject/build-impl.xml
@@ -46,51 +46,15 @@ is divided into following sections:
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Must set platform.home
- Must set platform.bootcp
- Must set platform.java
- Must set platform.javac
-
- The J2SE Platform is not correctly set up.
- Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files.
- Either open the project in the IDE and setup the Platform with the same name or add it manually.
- For example like this:
- ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file)
- or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used)
-
+
@@ -112,7 +76,7 @@ is divided into following sections:
-
+
@@ -190,6 +154,7 @@ is divided into following sections:
+
@@ -217,6 +182,20 @@ is divided into following sections:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -242,6 +221,7 @@ is divided into following sections:
+
@@ -284,7 +264,7 @@ is divided into following sections:
-
+
@@ -324,7 +304,7 @@ is divided into following sections:
-
+
@@ -405,7 +385,7 @@ is divided into following sections:
-
+
@@ -428,7 +408,7 @@ is divided into following sections:
-
+
@@ -460,7 +440,7 @@ is divided into following sections:
-
+
@@ -540,7 +520,7 @@ is divided into following sections:
-
+
@@ -565,7 +545,7 @@ is divided into following sections:
-
+
@@ -707,7 +687,7 @@ is divided into following sections:
-
+
@@ -742,9 +722,6 @@ is divided into following sections:
-
-
-
@@ -760,9 +737,7 @@ is divided into following sections:
-
-
-
+
@@ -787,7 +762,7 @@ is divided into following sections:
-
+
@@ -814,7 +789,7 @@ is divided into following sections:
-
+
@@ -853,7 +828,7 @@ is divided into following sections:
-
+
@@ -865,7 +840,7 @@ is divided into following sections:
-
+
@@ -988,15 +963,15 @@ is divided into following sections:
-
+
-
+
-
+
@@ -1004,7 +979,7 @@ is divided into following sections:
-
+
@@ -1012,7 +987,7 @@ is divided into following sections:
To run this application from the command line without Ant, try:
- ${platform.java} -jar "${dist.jar.resolved}"
+ java -jar "${dist.jar.resolved}"
@@ -1199,7 +1174,7 @@ is divided into following sections:
Must select one file in the IDE or set run.class
-
+ Must select one file in the IDE or set applet.url
@@ -1221,13 +1196,10 @@ is divided into following sections:
-
-
-
-
+
-
+
diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties
index 4d5b6615f0..6db8caa016 100644
--- a/nbproject/genfiles.properties
+++ b/nbproject/genfiles.properties
@@ -1,8 +1,8 @@
-build.xml.data.CRC32=92113194
+build.xml.data.CRC32=92efccf9
build.xml.script.CRC32=ff13faf5
-build.xml.stylesheet.CRC32=8064a381@1.75.2.48
+build.xml.stylesheet.CRC32=8064a381@1.80.1.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
-nbproject/build-impl.xml.data.CRC32=92113194
-nbproject/build-impl.xml.script.CRC32=cef58264
-nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48
+nbproject/build-impl.xml.data.CRC32=92efccf9
+nbproject/build-impl.xml.script.CRC32=8cda444e
+nbproject/build-impl.xml.stylesheet.CRC32=830a3534@1.80.1.48
diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties
index 67c9c27960..c1164614bd 100644
--- a/nbproject/private/private.properties
+++ b/nbproject/private/private.properties
@@ -3,4 +3,4 @@ do.depend=false
do.jar=true
javac.debug=true
javadoc.preview=true
-user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
+user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.2\\build.properties
diff --git a/nbproject/project.properties b/nbproject/project.properties
index 861f8eb51c..db7c8c2d82 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -47,10 +47,11 @@ javac.classpath=\
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
+javac.external.vm=false
javac.processorpath=\
${javac.classpath}
-javac.source=1.7
-javac.target=1.7
+javac.source=1.8
+javac.target=1.8
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
@@ -84,7 +85,7 @@ manifest.custom.permissions=
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
-platform.active=JDK_1.7
+platform.active=default_platform
project.license=gpl30_msv2
project.licensePath=./nbproject/licenseheader.txt
run.classpath=\
diff --git a/nbproject/project.xml b/nbproject/project.xml
index aed1c56350..980f89761c 100644
--- a/nbproject/project.xml
+++ b/nbproject/project.xml
@@ -4,7 +4,6 @@
HeavenMS
-
diff --git a/scripts/event/HenesysPQ.js b/scripts/event/HenesysPQ.js
index cc4ebb5061..18decbfa3d 100644
--- a/scripts/event/HenesysPQ.js
+++ b/scripts/event/HenesysPQ.js
@@ -106,7 +106,7 @@ function setup(level, lobbyid) {
eim.setProperty("level", level);
eim.setProperty("stage", "0");
eim.setProperty("bunnyCake", "0");
- eim.setProperty("bunnyDamage", "0");
+ eim.setProperty("bunnyDamaged", "0");
eim.getInstanceMap(910010000).resetPQ(level);
eim.getInstanceMap(910010000).allowSummonState(false);
diff --git a/scripts/npc/1032109.js b/scripts/npc/1032109.js
index 80c74e25b2..a21274ad52 100644
--- a/scripts/npc/1032109.js
+++ b/scripts/npc/1032109.js
@@ -9,6 +9,11 @@ var status;
var mobId = 2220100; //Blue Mushroom
function start(){
+ if (!cm.isQuestStarted(20718)) { // thanks Stray, Ari
+ cm.dispose();
+ return;
+ }
+
status = -1;
action(1, 0, 0);
}
diff --git a/scripts/quest/21738.js b/scripts/quest/21738.js
index 5e19fd7a27..04eb91b521 100644
--- a/scripts/quest/21738.js
+++ b/scripts/quest/21738.js
@@ -34,17 +34,22 @@ function start(mode, type, selection) {
else
status--;
- if (status == 0) {
- qm.sendGetText("Hm, what do you want?");
+ if (status == 0) { // thanks ZERO傑洛 for noticing this quest shouldn't need a pw -- GMS-like string data thanks to skycombat
+ qm.sendNext("What is it? I usually don't welcome uninvited guests, but you have a mysterious aura that makes me curious about what you have to say.", 9);
} else if (status == 1) {
- var text = qm.getText();
-
- if(text != "There's something strange going on in Orbis....") {
- qm.sendNext("No business to deal with? I won't brook loitering around here, go away.");
- qm.dispose();
- } else {
- qm.sendNext("Oh, that's right. I can sense the power emanating from you, as well. So I shall entrust something to you.");
- }
+ qm.sendNext("(You tell her about Giant Nependeath.)", 3);
+ } else if (status == 2) {
+ qm.sendNext("Giant Nependeath? It's definitely a big problem, but I don't think it's enough to really affect Orbis. Wait, where did you say the Giant Nependeath was, again?", 9);
+ } else if (status == 3) {
+ qm.sendNext("Neglected Strolling Path.", 3);
+ } else if (status == 4) {
+ qm.sendNext("...Neglected Strolling Path? If Giant Nependeath is there, someone is trying to enter Sealed Garden! But why? And more importantly, who?", 9);
+ } else if (status == 5) {
+ qm.sendNext("Sealed Garden?", 3);
+ } else if (status == 6) {
+ qm.sendAcceptDecline("I can't tell you about Sealed Garden. If you want to find out, I must first see whether you are worthy of the information. Do you mind if I look into your fate?", 9);
+ } else if (status == 7) {
+ qm.sendOk("Well, now let's look into your fate. Give me a second.");
} else {
qm.forceStartQuest();
qm.dispose();
diff --git a/sql/db_database.sql b/sql/db_database.sql
index 6bd4014580..3f106d8c6c 100644
--- a/sql/db_database.sql
+++ b/sql/db_database.sql
@@ -17406,7 +17406,7 @@ CREATE TABLE IF NOT EXISTS `reports` (
`victimid` int(11) NOT NULL,
`reason` tinyint(4) NOT NULL,
`chatlog` text NOT NULL,
- `status` text NOT NULL,
+ `description` text NOT NULL, # correct field name, thanks resinate
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java
index 1eb2a6c28d..ba413682f8 100644
--- a/src/client/MapleCharacter.java
+++ b/src/client/MapleCharacter.java
@@ -792,28 +792,31 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
return false;
}
+
+ public int calculateMaxBaseDamage(int watk, MapleWeaponType weapon) {
+ int mainstat, secondarystat;
+ if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) {
+ weapon = MapleWeaponType.DAGGER_THIEVES;
+ }
+
+ if (weapon == MapleWeaponType.BOW || weapon == MapleWeaponType.CROSSBOW || weapon == MapleWeaponType.GUN) {
+ mainstat = localdex;
+ secondarystat = localstr;
+ } else if (weapon == MapleWeaponType.CLAW || weapon == MapleWeaponType.DAGGER_THIEVES) {
+ mainstat = localluk;
+ secondarystat = localdex + localstr;
+ } else {
+ mainstat = localstr;
+ secondarystat = localdex;
+ }
+ return (int) (((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk);
+ }
public int calculateMaxBaseDamage(int watk) {
int maxbasedamage;
Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
if (weapon_item != null) {
- MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
- int mainstat, secondarystat;
- if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) {
- weapon = MapleWeaponType.DAGGER_THIEVES;
- }
-
- if (weapon == MapleWeaponType.BOW || weapon == MapleWeaponType.CROSSBOW || weapon == MapleWeaponType.GUN) {
- mainstat = localdex;
- secondarystat = localstr;
- } else if (weapon == MapleWeaponType.CLAW || weapon == MapleWeaponType.DAGGER_THIEVES) {
- mainstat = localluk;
- secondarystat = localdex + localstr;
- } else {
- mainstat = localstr;
- secondarystat = localdex;
- }
- maxbasedamage = (int) (((weapon.getMaxDamageMultiplier() * mainstat + secondarystat) / 100.0) * watk);
+ maxbasedamage = calculateMaxBaseDamage(watk, ii.getWeaponType(weapon_item.getItemId()));
} else {
if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
double weapMulti = 3;
@@ -830,8 +833,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
return maxbasedamage;
}
- public int calculateMaxBaseMagicDamage() {
- int maxbasedamage = getTotalMagic();
+ public int calculateMaxBaseMagicDamage(int matk) {
+ int maxbasedamage = matk;
int totalint = getTotalInt();
if (totalint > 2000) {
@@ -1110,6 +1113,29 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
}
+
+ private void broadcastChangeJob() {
+ for (MapleCharacter chr : map.getAllPlayers()) {
+ MapleClient chrC = chr.getClient();
+
+ if (chrC != null) { // propagate new job 3rd-person effects (FJ, Aran 1st strike, etc)
+ this.sendDestroyData(chrC);
+ this.sendSpawnData(chrC);
+ }
+ }
+
+ TimerManager.getInstance().schedule(new Runnable() { // need to delay to ensure clientside has finished reloading character data
+ @Override
+ public void run() {
+ MapleCharacter thisChr = MapleCharacter.this;
+ MapleMap map = thisChr.getMap();
+
+ if (map != null) {
+ map.broadcastMessage(thisChr, MaplePacketCreator.showForeignEffect(thisChr.getId(), 8), false);
+ }
+ }
+ }, 777);
+ }
public synchronized void changeJob(MapleJob newJob) {
if (newJob == null) {
@@ -1222,7 +1248,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
setMasteries(this.job.getId());
guildUpdate();
- getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(this.getId(), 8), false);
+ broadcastChangeJob();
if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) {
if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
@@ -7064,7 +7090,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
rs.close();
ps.close();
- ps = con.prepareStatement("SELECT name, characterslots FROM accounts WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
+ ps = con.prepareStatement("SELECT name, characterslots, language FROM accounts WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, ret.accountid);
rs = ps.executeQuery();
if (rs.next()) {
@@ -7072,6 +7098,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
retClient.setAccountName(rs.getString("name"));
retClient.setCharacterSlots(rs.getByte("characterslots"));
+ retClient.setLanguage(rs.getInt("language")); // thanks Zein for noticing user language not overriding default once player is in-game
}
rs.close();
ps.close();
diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java
index 67589efabb..149e8b88bf 100644
--- a/src/client/MapleClient.java
+++ b/src/client/MapleClient.java
@@ -1285,12 +1285,18 @@ public class MapleClient {
actionsSemaphore.release();
}
- public void lockEncoder() {
- encoderLock.lock();
+ public boolean tryacquireEncoder() {
+ if (actionsSemaphore.tryAcquire()) {
+ encoderLock.lock();
+ return true;
+ } else {
+ return false;
+ }
}
public void unlockEncoder() {
encoderLock.unlock();
+ actionsSemaphore.release();
}
private static class CharNameAndId {
diff --git a/src/client/SkillFactory.java b/src/client/SkillFactory.java
index 8e0e5398c4..1bd4aa0c63 100644
--- a/src/client/SkillFactory.java
+++ b/src/client/SkillFactory.java
@@ -180,6 +180,7 @@ public class SkillFactory {
case NightWalker.POISON_BOMB:
case NightWalker.VAMPIRE:
case ChiefBandit.CHAKRA:
+ case Aran.COMBAT_STEP:
case Evan.RECOVERY_AURA:
isBuff = false;
break;
diff --git a/src/client/command/CommandsExecutor.java b/src/client/command/CommandsExecutor.java
index b9d6e0fc6e..547f21c771 100644
--- a/src/client/command/CommandsExecutor.java
+++ b/src/client/command/CommandsExecutor.java
@@ -265,6 +265,7 @@ public class CommandsExecutor {
addCommand("unbug", 2, UnBugCommand.class);
addCommand("id", 2, IdCommand.class);
addCommand("gachalist", GachaListCommand.class);
+ addCommand("loot", LootCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}
diff --git a/src/client/command/commands/gm3/QuestCompleteCommand.java b/src/client/command/commands/gm3/QuestCompleteCommand.java
index 3ea851b0f9..358d6642da 100644
--- a/src/client/command/commands/gm3/QuestCompleteCommand.java
+++ b/src/client/command/commands/gm3/QuestCompleteCommand.java
@@ -26,6 +26,7 @@ package client.command.commands.gm3;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
+import server.quest.MapleQuest;
public class QuestCompleteCommand extends Command {
{
@@ -44,7 +45,13 @@ public class QuestCompleteCommand extends Command {
int questId = Integer.parseInt(params[0]);
if (player.getQuestStatus(questId) == 1) {
- c.getAbstractPlayerInteraction().forceCompleteQuest(questId);
+ MapleQuest quest = MapleQuest.getInstance(questId);
+ if (quest != null && quest.getNpcRequirement(true) != -1) {
+ c.getAbstractPlayerInteraction().forceCompleteQuest(questId, quest.getNpcRequirement(true));
+ } else {
+ c.getAbstractPlayerInteraction().forceCompleteQuest(questId);
+ }
+
player.dropMessage(5, "QUEST " + questId + " completed.");
} else {
player.dropMessage(5, "QUESTID " + questId + " not started or already completed.");
diff --git a/src/client/command/commands/gm3/QuestStartCommand.java b/src/client/command/commands/gm3/QuestStartCommand.java
index c609e66d64..831ab05b2e 100644
--- a/src/client/command/commands/gm3/QuestStartCommand.java
+++ b/src/client/command/commands/gm3/QuestStartCommand.java
@@ -26,6 +26,7 @@ package client.command.commands.gm3;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
+import server.quest.MapleQuest;
public class QuestStartCommand extends Command {
{
@@ -44,7 +45,13 @@ public class QuestStartCommand extends Command {
int questid = Integer.parseInt(params[0]);
if (player.getQuestStatus(questid) == 0) {
- c.getAbstractPlayerInteraction().forceStartQuest(questid);
+ MapleQuest quest = MapleQuest.getInstance(questid);
+ if (quest != null && quest.getNpcRequirement(false) != -1) {
+ c.getAbstractPlayerInteraction().forceStartQuest(questid, quest.getNpcRequirement(false));
+ } else {
+ c.getAbstractPlayerInteraction().forceStartQuest(questid);
+ }
+
player.dropMessage(5, "QUEST " + questid + " started.");
} else {
player.dropMessage(5, "QUESTID " + questid + " already started/completed.");
diff --git a/src/constants/skills/Aran.java b/src/constants/skills/Aran.java
index 75a857c2bb..7a61ceeddb 100644
--- a/src/constants/skills/Aran.java
+++ b/src/constants/skills/Aran.java
@@ -29,6 +29,7 @@ public class Aran {
public static final int DOUBLE_SWING = 21000002;
public static final int TRIPLE_SWING = 21100001;
public static final int COMBO_ABILITY = 21000000;
+ public static final int COMBAT_STEP = 21001001;
public static final int POLEARM_BOOSTER = 21001003;
public static final int MAPLE_WARRIOR = 21121000;
public static final int FREEZE_STANDING = 21121003;
diff --git a/src/net/mina/MaplePacketEncoder.java b/src/net/mina/MaplePacketEncoder.java
index 9887244c1d..cad289e166 100644
--- a/src/net/mina/MaplePacketEncoder.java
+++ b/src/net/mina/MaplePacketEncoder.java
@@ -24,7 +24,6 @@ package net.mina;
import constants.ServerConstants;
import client.MapleClient;
import constants.OpcodeConstants;
-import net.opcodes.SendOpcode;
import net.server.coordinator.MapleSessionCoordinator;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
@@ -43,40 +42,41 @@ public class MaplePacketEncoder implements ProtocolEncoder {
final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
try {
- client.lockEncoder();
- try {
- final MapleAESOFB send_crypto = client.getSendCrypto();
- final byte[] input = (byte[]) message;
- if (ServerConstants.USE_DEBUG_SHOW_PACKET) {
- int packetLen = input.length;
- int pHeader = readFirstShort(input);
- String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
- String op = lookupRecv(pHeader);
- String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
- if (packetLen <= 50000) {
- String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input);
- System.out.println(RecvTo);
- if (op == null) {
- System.out.println("UnknownPacket:" + RecvTo);
+ if (client.tryacquireEncoder()) {
+ try {
+ final MapleAESOFB send_crypto = client.getSendCrypto();
+ final byte[] input = (byte[]) message;
+ if (ServerConstants.USE_DEBUG_SHOW_PACKET) {
+ int packetLen = input.length;
+ int pHeader = readFirstShort(input);
+ String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
+ String op = lookupRecv(pHeader);
+ String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
+ if (packetLen <= 50000) {
+ String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input);
+ System.out.println(RecvTo);
+ if (op == null) {
+ System.out.println("UnknownPacket:" + RecvTo);
+ }
+ } else {
+ FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ...");
}
- } else {
- FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ...");
}
+
+ final byte[] unencrypted = new byte[input.length];
+ System.arraycopy(input, 0, unencrypted, 0, input.length);
+ final byte[] ret = new byte[unencrypted.length + 4];
+ final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
+ MapleCustomEncryption.encryptData(unencrypted);
+
+ send_crypto.crypt(unencrypted);
+ System.arraycopy(header, 0, ret, 0, 4);
+ System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
+
+ out.write(IoBuffer.wrap(ret));
+ } finally {
+ client.unlockEncoder();
}
-
- final byte[] unencrypted = new byte[input.length];
- System.arraycopy(input, 0, unencrypted, 0, input.length);
- final byte[] ret = new byte[unencrypted.length + 4];
- final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
- MapleCustomEncryption.encryptData(unencrypted);
-
- send_crypto.crypt(unencrypted);
- System.arraycopy(header, 0, ret, 0, 4);
- System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
-
- out.write(IoBuffer.wrap(ret));
- } finally {
- client.unlockEncoder();
}
// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
// out.write(ByteBuffer.wrap(ret));
diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java
index 1c14e708b5..4433d65d4c 100644
--- a/src/net/server/channel/Channel.java
+++ b/src/net/server/channel/Channel.java
@@ -460,6 +460,7 @@ public final class Channel {
}
expeditions.put(exped.getType(), exped);
+ exped.beginRegistration(); // thanks Conrad for noticing leader still receiving packets on failure-to-register cases
return true;
}
}
diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java
index 99f92ffbb8..8162296a1e 100644
--- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java
+++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java
@@ -134,8 +134,9 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
}
- protected synchronized void applyAttack(AttackInfo attack, final MapleCharacter player, int attackCount) {
- if (player.getMap().isOwnershipRestricted(player)) {
+ protected void applyAttack(AttackInfo attack, final MapleCharacter player, int attackCount) {
+ final MapleMap map = player.getMap();
+ if (map.isOwnershipRestricted(player)) {
return;
}
@@ -150,7 +151,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
theSkill = SkillFactory.getSkill(GameConstants.getHiddenSkill(attack.skill)); //returns back the skill id if its not a hidden skill so we are gucci
attackEffect = attack.getAttackEffect(player, theSkill);
if (attackEffect == null) {
- player.getClient().announce(MaplePacketCreator.enableActions());
+ player.announce(MaplePacketCreator.enableActions());
return;
}
@@ -176,7 +177,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}
}
} else {
- player.getClient().announce(MaplePacketCreator.enableActions());
+ player.announce(MaplePacketCreator.enableActions());
}
}
@@ -195,7 +196,6 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
}*/
int totDamage = 0;
- final MapleMap map = player.getMap();
if (attack.skill == ChiefBandit.MESO_EXPLOSION) {
int delay = 0;
@@ -308,7 +308,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
- player.getMap().spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, (double) 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2);
+ map.spawnMesoDrop(Math.min((int) Math.max(((double) eachdf / (double) 20000) * (double) maxmeso, (double) 1), maxmeso), new Point((int) (monster.getPosition().getX() + Randomizer.nextInt(100) - 50), (int) (monster.getPosition().getY())), monster, player, true, (byte) 2);
}
}, delay);
delay += 100;
@@ -333,7 +333,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
List toSteal = new ArrayList<>();
toSteal.add(mi.retrieveDrop(monster.getId()).get(i));
- player.getMap().dropItemsFromMonster(toSteal, player, monster);
+ map.dropItemsFromMonster(toSteal, player, monster);
monster.addStolen(toSteal.get(0).itemId);
}
}
diff --git a/src/net/server/channel/handlers/HealOvertimeHandler.java b/src/net/server/channel/handlers/HealOvertimeHandler.java
index 70c5157c48..dec1b5c1b9 100644
--- a/src/net/server/channel/handlers/HealOvertimeHandler.java
+++ b/src/net/server/channel/handlers/HealOvertimeHandler.java
@@ -25,6 +25,7 @@ import client.MapleCharacter;
import client.MapleClient;
import client.autoban.AutobanFactory;
import client.autoban.AutobanManager;
+import constants.GameConstants;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.maps.MapleMapFactory;
@@ -46,7 +47,7 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler {
abm.setTimestamp(8, timestamp, 28); // thanks Vcoc & Thora for pointing out d/c happening here
if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing");
- int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa...
+ int abHeal = (int)(77 * MapleMapFactory.getMapRecoveryRate(chr.getMapId()) * 1.5); // thanks Ari for noticing players not getting healed in sauna in certain cases
if (healHP > abHeal) {
AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + ".");
return;
diff --git a/src/net/server/channel/handlers/MoveDragonHandler.java b/src/net/server/channel/handlers/MoveDragonHandler.java
index 16c8422407..0b8bb9f099 100644
--- a/src/net/server/channel/handlers/MoveDragonHandler.java
+++ b/src/net/server/channel/handlers/MoveDragonHandler.java
@@ -37,14 +37,16 @@ public class MoveDragonHandler extends AbstractMovementPacketHandler {
final Point startPos = new Point(slea.readShort(), slea.readShort());
long movementDataStart = slea.getPosition();
final MapleDragon dragon = chr.getDragon();
- updatePosition(slea, dragon, 0);
- long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
- if (dragon != null && movementDataLength > 0) {
- slea.seek(movementDataStart);
- if (chr.isHidden()) {
- chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength));
- } else {
- chr.getMap().broadcastMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength), dragon.getPosition());
+ if (dragon != null) {
+ updatePosition(slea, dragon, 0);
+ long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
+ if (movementDataLength > 0) {
+ slea.seek(movementDataStart);
+ if (chr.isHidden()) {
+ chr.getMap().broadcastGMMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength));
+ } else {
+ chr.getMap().broadcastMessage(chr, MaplePacketCreator.moveDragon(dragon, startPos, slea, movementDataLength), dragon.getPosition());
+ }
}
}
}
diff --git a/src/net/server/channel/handlers/MoveLifeHandler.java b/src/net/server/channel/handlers/MoveLifeHandler.java
index 245d18279e..67d6704ed9 100644
--- a/src/net/server/channel/handlers/MoveLifeHandler.java
+++ b/src/net/server/channel/handlers/MoveLifeHandler.java
@@ -143,14 +143,15 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
Point startPos = new Point(start_x, start_y - 2);
Point serverStartPos = new Point(monster.getPosition());
long movementDataStart = slea.getPosition();
- updatePosition(slea, monster, -2);
- long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
-
- Boolean aggro = monster.aggroMoveLifeUpdate(player);
- if (aggro == null) return;
-
- if (nextUse != null) {
- c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, nextSkillId, nextSkillLevel));
+
+ updatePosition(slea, monster, -2); // Thanks Doodle and ZERO傑洛 for noticing sponge-based bosses moving out of stage in case of no-offset applied
+ long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
+
+ Boolean aggro = monster.aggroMoveLifeUpdate(player);
+ if (aggro == null) return;
+
+ if (nextUse != null) {
+ c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro, nextSkillId, nextSkillLevel));
} else {
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, mobMp, aggro));
}
diff --git a/src/net/server/channel/handlers/MoveSummonHandler.java b/src/net/server/channel/handlers/MoveSummonHandler.java
index 3ea5c0118b..911ffd2ce8 100644
--- a/src/net/server/channel/handlers/MoveSummonHandler.java
+++ b/src/net/server/channel/handlers/MoveSummonHandler.java
@@ -44,10 +44,10 @@ public final class MoveSummonHandler extends AbstractMovementPacketHandler {
break;
}
}
- long movementDataStart = slea.getPosition();
- updatePosition(slea, summon, 0);
- long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
if (summon != null) {
+ long movementDataStart = slea.getPosition();
+ updatePosition(slea, summon, 0);
+ long movementDataLength = slea.getPosition() - movementDataStart; //how many bytes were read by updatePosition
slea.seek(movementDataStart);
player.getMap().broadcastMessage(player, MaplePacketCreator.moveSummon(player.getId(), oid, startPos, slea, movementDataLength), summon.getPosition());
}
diff --git a/src/net/server/channel/handlers/PetExcludeItemsHandler.java b/src/net/server/channel/handlers/PetExcludeItemsHandler.java
index 4b1842c51a..b0619ac4fe 100644
--- a/src/net/server/channel/handlers/PetExcludeItemsHandler.java
+++ b/src/net/server/channel/handlers/PetExcludeItemsHandler.java
@@ -33,6 +33,8 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Ronan
*/
public final class PetExcludeItemsHandler extends AbstractMaplePacketHandler {
+
+ @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
final int petId = slea.readInt();
slea.skip(4);
diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java
index b51161b6ce..1698aa3732 100644
--- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java
+++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java
@@ -203,6 +203,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
if (!newcomer) {
+ c.setLanguage(player.getClient().getLanguage());
c.setCharacterSlots((byte) player.getClient().getCharacterSlots());
player.newClient(c);
}
diff --git a/src/net/server/channel/handlers/SummonDamageHandler.java b/src/net/server/channel/handlers/SummonDamageHandler.java
index 08315c3a0b..4972eefa73 100644
--- a/src/net/server/channel/handlers/SummonDamageHandler.java
+++ b/src/net/server/channel/handlers/SummonDamageHandler.java
@@ -26,9 +26,14 @@ import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import client.autoban.AutobanFactory;
+import client.inventory.Item;
+import client.inventory.MapleInventoryType;
+import client.inventory.MapleWeaponType;
import client.status.MonsterStatusEffect;
+import constants.skills.Outlaw;
import java.util.ArrayList;
import java.util.List;
+import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import server.life.MapleMonster;
import server.life.MapleMonsterInformationProvider;
@@ -115,15 +120,29 @@ public final class SummonDamageHandler extends AbstractDealDamageHandler {
player.getMap().damageMonster(player, target, damage);
}
}
+
+ if (summon.getSkill() == Outlaw.GAVIOTA) { // thanks Periwinks for noticing Gaviota not cancelling after grenade toss
+ player.cancelEffect(summonEffect, false, -1);
+ }
}
private static int calcMaxDamage(MapleStatEffect summonEffect, MapleCharacter player, boolean magic) {
double maxDamage;
if (magic) {
- maxDamage = player.calculateMaxBaseMagicDamage() * (0.05 * summonEffect.getMatk());
+ int matk = Math.max(player.getTotalMagic(), 14);
+ maxDamage = player.calculateMaxBaseMagicDamage(matk) * (0.05 * summonEffect.getMatk());
} else {
- int maxBaseDmg = player.calculateMaxBaseDamage(player.getTotalWatk()); // thanks Conrad for detecting some summons legitimately hitting over the calculated limit
+ int watk = Math.max(player.getTotalWatk(), 14);
+ Item weapon_item = player.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
+
+ int maxBaseDmg; // thanks Conrad, Atoot for detecting some summons legitimately hitting over the calculated limit
+ if (weapon_item != null) {
+ maxBaseDmg = player.calculateMaxBaseDamage(watk, MapleItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId()));
+ } else {
+ maxBaseDmg = player.calculateMaxBaseDamage(watk, MapleWeaponType.SWORD1H);
+ }
+
float summonDmgMod = (maxBaseDmg >= 438) ? 0.054f : 0.077f;
maxDamage = maxBaseDmg * (summonDmgMod * summonEffect.getWatk());
}
diff --git a/src/net/server/coordinator/MapleMatchCheckerCoordinator.java b/src/net/server/coordinator/MapleMatchCheckerCoordinator.java
index a50fe4ac37..79cef27403 100644
--- a/src/net/server/coordinator/MapleMatchCheckerCoordinator.java
+++ b/src/net/server/coordinator/MapleMatchCheckerCoordinator.java
@@ -300,7 +300,7 @@ public class MapleMatchCheckerCoordinator {
}
private void disposeMatchElement(MapleMatchCheckingElement mmce) {
- Set matchPlayers = mmce.getMatchPlayers();
+ Set matchPlayers = mmce.getMatchPlayers(); // thanks Ai for noticing players getting match-stuck on certain cases
while (!poolMatchPlayers(matchPlayers)) {
try {
Thread.sleep(1000);
diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java
index 8fdaaa4c3e..fa1df90ca4 100644
--- a/src/net/server/world/World.java
+++ b/src/net/server/world/World.java
@@ -47,6 +47,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
@@ -130,7 +131,7 @@ public class World {
private Set queuedGuilds = new HashSet<>();
private Map, Pair>> queuedMarriages = new HashMap<>();
- private Map> marriageGuests = new HashMap<>();
+ private Map> marriageGuests = new ConcurrentHashMap<>();
private Map partyChars = new HashMap<>();
private Map parties = new HashMap<>();
@@ -720,7 +721,7 @@ public class World {
return new Pair<>(type, guests);
}
- public synchronized boolean addMarriageGuest(int marriageId, int playerId) {
+ public boolean addMarriageGuest(int marriageId, int playerId) {
Set guests = marriageGuests.get(marriageId);
if(guests != null) {
if(guests.contains(playerId)) return false;
diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java
index 63177dc9e1..b6f8c031a6 100644
--- a/src/scripting/AbstractPlayerInteraction.java
+++ b/src/scripting/AbstractPlayerInteraction.java
@@ -236,25 +236,43 @@ public class AbstractPlayerInteraction {
return canHoldAllAfterRemoving(Collections.singletonList(itemid), Collections.singletonList(quantity), Collections.singletonList(removeItemid), Collections.singletonList(removeQuantity));
}
- private List convertToIntegerArray(List list) {
- List intList = new LinkedList<>();
- for(Double d: list) {
- intList.add(d.intValue());
+ private List convertToIntegerArray(List