Storybook announce + AOE Mobskills & Autopot & Inventory check Patch
Implemented storybook detection for the skillbook announcer. Fixed an issue with AOE mob skills not locating players properly in certain situations. Reviewed interaction of players within same remote network on multiclient system. Patched messages when leaving the expedition area showing up for leaving players. Patched some skill questline item chances unexpectedly too low. Reviewed Rush questline-exclusive area not allowing multiple players in the room. Fixed meso capacity check on shop owner happening after item transaction. Patched server-side autopot settings, now using threshold cooling method to determine a player's updated settings. Reviewed locking flow regarding world/channel deployment on main class. Fixed a scenario where items collectable by party members would not show up in case those were the only items to be dropped and none needed by the last-hitter. Patched an issue in the inventory space checking algorithm that would allow a transaction for multiple of the same equipments. Refactored Dojo entry/exit modules. Patched a check on Maker skill. Fixed Cygnus FA buff getting reapplied each time the skill procs.
This commit is contained in:
+1
-1
@@ -193,7 +193,7 @@ server:
|
||||
#Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses.
|
||||
|
||||
#Multiclient Coordinator Configuration
|
||||
MAX_ALLOWED_ACCOUNT_HWID: 4 #Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login.
|
||||
MAX_ALLOWED_ACCOUNT_HWID: 10 #Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login.
|
||||
MAX_ACCOUNT_LOGIN_ATTEMPT: 15 #After N tries on an account, login on that account gets disabled for a short period.
|
||||
LOGIN_ATTEMPT_DURATION: 120 #Period in seconds the login attempt remains registered on the system.
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ Known issues:
|
||||
- If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen.
|
||||
- Sometimes battleship may behave oddly with the enhanced buff system, making the character d/c in certain scenarios.
|
||||
- Dragon Roar doesn't show the stun effect to players.
|
||||
- Cygnus job 'Final Attack' skills not functional.
|
||||
- Cygnus job 'Final Attack' skill for Wind Archer not functional.
|
||||
- Steal skill doesn't deduct the loot from the drop pool from a mob.
|
||||
- Snipe will show much higher damage value than actually applicable to the attacker.
|
||||
- Some monster status such as weapon/magic reflect doesn't behave properly in certain scenarios.
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
Dalair -> 9000040
|
||||
Donation Box -> 9000041
|
||||
Abdula -> 9209000 *
|
||||
MapleTV -> scroll_generator
|
||||
|
||||
CUSTOM NPC SHOPS (db_shopupdate.sql):
|
||||
Asia -> 2082014
|
||||
@@ -2273,3 +2274,35 @@ Corrigido caso inesperado em 2nd job de pirata bloqueando saída de jogadores do
|
||||
Corrigido problema no sistema de matching ao tentar rodar ações externas enquanto travando os recursos do sistema, ao criar match.
|
||||
Corrigido caso onde novos jogadores poderiam ser agregados à party e entrar em campo na MCPQ assim que confirmação de partida e contagem de início fossem efetivados.
|
||||
Adicionado cache para requerimento de scrolls, assim melhorando tempo de resposta para o novo custom NPC de geração de scrolls.
|
||||
|
||||
15 Novembro 2019,
|
||||
Reajustado chance de drops de vários livros de quest para skills de 4o job.
|
||||
|
||||
18 Novembro 2019,
|
||||
Corrigido atividade recente evitando skills em área de mobs não acertando mais diversos jogadores.
|
||||
Corrigido uso de facingLeft em aplicação de mobskills levando a cálculos inviáveis de área de atuação.
|
||||
Aprimorado sistema multi-cliente, agora permitindo login de jogadores dentro de uma mesma rede.
|
||||
Revisado mensagens de expedição finalizando abruptamente aparecendo para jogador que está saindo do evento.
|
||||
Revisado posicionamento de laranja na loja da NPC Miki.
|
||||
Corrigido drop de quest de mineiros que deveria aparecer frequentemente dropando muito raramente.
|
||||
Revisado área restrita de quest para skill Rush não permitindo acesso a vários jogadores simultaneamente.
|
||||
Corrigido ocorrência de loop contínuo no sistema de matching, para casos com mais de um respondente.
|
||||
Corrigido checagem por mesos de dono das Player Shops ocorrendo após inserção do produto no inventário.
|
||||
Corrigido alerta de autopots atuando sempre, uma vez que configurações foram setadas no máximo. Modelo agora passa a usar esfriamento de limites para reaquisição dos limites do lado cliente.
|
||||
|
||||
19 - 20 Novembro 2019,
|
||||
Revisado abordagem de locks de canal e mundo na classe principal.
|
||||
Implementado detecção por questbooks pelo anunciador de skillbooks.
|
||||
Adicionado buffs com bonus na contabilização de dano teto feita no handler de danos aplicados.
|
||||
Implementado detecção de skills recompensadas por quests além dos itens de skillbooks pelo anunciador.
|
||||
Corrigido caso onde drops de quest não estavam aparecendo caso fossem únicos na lista de itens a aparecer e last hitter não precisasse dos mesmos.
|
||||
|
||||
22 - 23 Novembro 2019,
|
||||
Corrigido caso de jogador entrando em estado inconsistente com storage de jogadores ao mudar de mapas.
|
||||
Corrigido bug no método de checagem de quantidade de slots no inventário para múltiplos do mesmo equipamento.
|
||||
Corrigido buff Final Attack de Cygnus sendo reaplicado a todo acerto de skill.
|
||||
|
||||
24 - 25 Novembro 2019,
|
||||
Corrigido caso não sendo checado devidamente com Maker.
|
||||
Corrigido contagem de projéteis nos stats de skill usando tipo de dados de tamanho insuficiente.
|
||||
Refatorado acesso a membros relativos a Dojo em canais de forma a buscar melhorar efetividade dos ingressos e liberações de lobby.
|
||||
@@ -242,8 +242,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -310,8 +310,8 @@ function playerRevive(eim, player) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -127,8 +127,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -156,8 +156,8 @@ function playerRevive(eim, player) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -129,8 +129,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -170,8 +170,8 @@ function monsterRevive(eim, mob) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -113,8 +113,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -142,8 +142,8 @@ function playerRevive(eim, player) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -120,8 +120,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -151,8 +151,8 @@ function playerRevive(eim, player) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -117,8 +117,8 @@ function scheduledTimeout(eim) {
|
||||
function changedMap(eim, player, mapid) {
|
||||
if (mapid < minMapId || mapid > maxMapId) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
@@ -146,8 +146,8 @@ function playerRevive(eim, player) {
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isExpeditionTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
eim.unregisterPlayer(player);
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
end(eim);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -31,15 +31,12 @@ function start(ms) {
|
||||
var stage = Math.floor(ms.getMapId() / 100) % 100;
|
||||
var callBoss = false;
|
||||
|
||||
if (stage % 6 == 1) {
|
||||
ms.getClient().getChannelServer().startDojoSchedule(ms.getMapId());
|
||||
} else if(stage % 6 == 0) {
|
||||
if (stage % 6 == 0) {
|
||||
ms.getClient().getChannelServer().dismissDojoSchedule(ms.getMapId(), ms.getParty());
|
||||
}
|
||||
|
||||
ms.getClient().getChannelServer().setDojoProgress(ms.getMapId());
|
||||
} else {
|
||||
callBoss = ms.getClient().getChannelServer().setDojoProgress(ms.getMapId());
|
||||
|
||||
if (stage % 6 > 0) {
|
||||
var realstage = stage - ((stage / 6) | 0);
|
||||
var mob = ms.getMonsterLifeFactory(9300183 + realstage);
|
||||
if (callBoss && mob != null && ms.getPlayer().getMap().getMonsterById(9300216) == null) {
|
||||
|
||||
+9
-10
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
importPackage(Packages.config);
|
||||
importPackage(Packages.constants.game);
|
||||
|
||||
var disabled = false;
|
||||
var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004);
|
||||
@@ -69,7 +70,7 @@ function action(mode, type, selection) {
|
||||
if(status == 0) {
|
||||
if (isRestingSpot(cm.getPlayer().getMap().getId())) {
|
||||
var text = "I'm surprised you made it this far! But it won't be easy from here on out. You still want the challenge?\r\n\r\n#b#L0#I want to continue#l\r\n#L1#I want to leave#l\r\n";
|
||||
if (!cm.getPlayer().getDojoParty()) {
|
||||
if (!GameConstants.isDojoPartyArea(cm.getPlayer().getMapId())) {
|
||||
text += "#L2#I want to record my score up to this point#l";
|
||||
}
|
||||
cm.sendSimple(text);
|
||||
@@ -99,7 +100,7 @@ function action(mode, type, selection) {
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
var avDojo = cm.getClient().getChannelServer().getAvailableDojo(true);
|
||||
var avDojo = cm.getClient().getChannelServer().ingressDojo(true, 0);
|
||||
|
||||
if(avDojo < 0) {
|
||||
if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again.");
|
||||
@@ -107,7 +108,6 @@ function action(mode, type, selection) {
|
||||
}
|
||||
else {
|
||||
cm.getClient().getChannelServer().getMapFactory().getMap(925020010 + avDojo).resetMapObjects();
|
||||
cm.getClient().getChannelServer().resetDojo(925020010 + avDojo);
|
||||
|
||||
cm.resetDojoEnergy();
|
||||
cm.warp(925020010 + avDojo, 0);
|
||||
@@ -120,9 +120,11 @@ function action(mode, type, selection) {
|
||||
} else if (cm.getPlayer().getDojoStage() > 0) {
|
||||
dojoWarp = cm.getPlayer().getDojoStage();
|
||||
cm.getPlayer().setDojoStage(0);
|
||||
cm.sendYesNo("The last time you took the challenge by yourself, you went up to level #b" + dojoWarp + "#k. I can take you there right now. Do you want to go there? (Select #rNo#k to erase this record.)");
|
||||
|
||||
var stageWarp = ((dojoWarp / 6) | 0) * 5;
|
||||
cm.sendYesNo("The last time you took the challenge by yourself, you went up to round #b" + stageWarp + "#k. I can take you there right now. Do you want to go there? (Select #rNo#k to erase this record.)");
|
||||
} else {
|
||||
var avDojo = cm.getClient().getChannelServer().getAvailableDojo(false);
|
||||
var avDojo = cm.getClient().getChannelServer().ingressDojo(false, dojoWarp);
|
||||
|
||||
if(avDojo < 0) {
|
||||
if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again.");
|
||||
@@ -132,7 +134,6 @@ function action(mode, type, selection) {
|
||||
} else {
|
||||
var warpDojoMap = 925020000 + (dojoWarp + 1) * 100 + avDojo;
|
||||
cm.getClient().getChannelServer().resetDojoMap(warpDojoMap);
|
||||
cm.getClient().getChannelServer().resetDojo(warpDojoMap);
|
||||
|
||||
cm.resetDojoEnergy();
|
||||
cm.warp(warpDojoMap, 0);
|
||||
@@ -164,14 +165,13 @@ function action(mode, type, selection) {
|
||||
cm.dispose();
|
||||
return;
|
||||
} else {
|
||||
var avDojo = cm.getClient().getChannelServer().getAvailableDojo(true, cm.getParty());
|
||||
var avDojo = cm.getClient().getChannelServer().ingressDojo(true, cm.getParty(), 0);
|
||||
|
||||
if(avDojo < 0) {
|
||||
if(avDojo == -1) cm.sendOk("All Dojo's are being used already. Wait for awhile before trying again.");
|
||||
else cm.sendOk("Either your party is already using the Dojo or your party's allotted time on the Dojo has not expired yet. Wait for them to finish to enter.");
|
||||
} else {
|
||||
cm.getClient().getChannelServer().resetDojoMap(925030100 + avDojo);
|
||||
cm.getClient().getChannelServer().resetDojo(925030100 + avDojo);
|
||||
|
||||
cm.resetPartyDojoEnergy();
|
||||
cm.warpParty(925030100 + avDojo);
|
||||
@@ -299,7 +299,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
}
|
||||
|
||||
avDojo = cm.getClient().getChannelServer().getAvailableDojo(hasParty, cm.getParty());
|
||||
avDojo = cm.getClient().getChannelServer().ingressDojo(hasParty, cm.getParty(), Math.floor((cm.getPlayer().getMap().getId()) / 100) % 100);
|
||||
firstEnter = true;
|
||||
}
|
||||
|
||||
@@ -313,7 +313,6 @@ function action(mode, type, selection) {
|
||||
var dojoWarpMap = baseStg + (nextStg * 100) + avDojo;
|
||||
if(firstEnter) {
|
||||
cm.getClient().getChannelServer().resetDojoMap(dojoWarpMap);
|
||||
cm.getClient().getChannelServer().resetDojo(dojoWarpMap, nextStg - 1);
|
||||
}
|
||||
|
||||
//non-leader party members can progress whilst having the record saved if they don't command to enter the next stage
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
* @Map(s): Dojo Hall
|
||||
*/
|
||||
|
||||
importPackage(Packages.constants.game);
|
||||
|
||||
var disabled = false;
|
||||
var belts = Array(1132000, 1132001, 1132002, 1132003, 1132004);
|
||||
var belt_level = Array(25, 35, 45, 60, 75);
|
||||
@@ -45,7 +47,7 @@ function start() {
|
||||
|
||||
if (isRestingSpot(cm.getPlayer().getMap().getId())) {
|
||||
var text = "I'm surprised you made it this far! But it won't be easy from here on out. You still want the challenge?\r\n\r\n#b#L0#I want to continue#l\r\n#L1#I want to leave#l\r\n";
|
||||
if (!cm.getPlayer().getDojoParty()) {
|
||||
if (!GameConstants.isDojoPartyArea(cm.getPlayer().getMapId())) {
|
||||
text += "#L2#I want to record my score up to this point#l";
|
||||
}
|
||||
cm.sendSimple(text);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
* @author Ronan Lana
|
||||
*/
|
||||
|
||||
importPackage(Packages.client.processor);
|
||||
importPackage(Packages.client.processor.action);
|
||||
importPackage(Packages.config);
|
||||
|
||||
var status;
|
||||
|
||||
+23
-3
@@ -88,18 +88,34 @@ function action(mode, type, selection) {
|
||||
var sendStr = "The following books are currently available:\r\n\r\n";
|
||||
if(selected == 0) selected = selection;
|
||||
|
||||
table = (selected == 1) ? skillbook : masterybook;
|
||||
if (selected == 1) {
|
||||
table = skillbook;
|
||||
for(var i = 0; i < table.length; i++) {
|
||||
sendStr += " #L" + i + "# #i" + table[i] + "# #t" + table[i] + "##l\r\n";
|
||||
if (table[i] > 0) {
|
||||
var itemid = table[i];
|
||||
sendStr += " #L" + i + "# #i" + itemid + "# #t" + itemid + "##l\r\n";
|
||||
} else {
|
||||
var skillid = -table[i];
|
||||
sendStr += " #L" + i + "# #s" + skillid + "# #q" + skillid + "##l\r\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
table = masterybook;
|
||||
for(var i = 0; i < table.length; i++) {
|
||||
var itemid = table[i];
|
||||
sendStr += " #L" + i + "# #i" + itemid + "# #t" + itemid + "##l\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
cm.sendSimple(sendStr);
|
||||
|
||||
} else if(status == 2) {
|
||||
selected = selection;
|
||||
var mobList = cm.getNamesWhoDropsItem(table[selected]);
|
||||
|
||||
var sendStr;
|
||||
if (table[selected] > 0) {
|
||||
var mobList = cm.getNamesWhoDropsItem(table[selected]);
|
||||
|
||||
if(mobList.length == 0) {
|
||||
sendStr = "No mobs drop '#b#t" + table[selected] + "##k'.\r\n\r\n";
|
||||
} else {
|
||||
@@ -111,6 +127,10 @@ function action(mode, type, selection) {
|
||||
|
||||
sendStr += "\r\n";
|
||||
}
|
||||
} else {
|
||||
sendStr = "\r\n\r\n";
|
||||
}
|
||||
|
||||
sendStr += cm.getSkillBookInfo(table[selected]);
|
||||
|
||||
cm.sendNext(sendStr);
|
||||
|
||||
@@ -13,7 +13,7 @@ var name_cursor, role_cursor;
|
||||
|
||||
// new server names are to be appended at the start of the name stack, building up the chronology.
|
||||
// make sure the server names are lexicograffically equivalent to their correspondent function.
|
||||
var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "MetroMS", "BubblesDEV", "OdinMS", "Contributors"];
|
||||
var servers = ["HeavenMS", "MapleSolaxia", "MoopleDEV", "BubblesDEV", "MetroMS", "OdinMS", "Contributors"];
|
||||
var servers_history = [];
|
||||
|
||||
function addPerson(name, role) {
|
||||
@@ -65,20 +65,20 @@ function writeServerStaff_MoopleDEV() {
|
||||
setHistory(2010, 2012);
|
||||
}
|
||||
|
||||
function writeServerStaff_MetroMS() {
|
||||
addPerson("David!", "Developer");
|
||||
addPerson("XxOsirisxX", "Contributor");
|
||||
addPerson("Generic", "Contributor");
|
||||
|
||||
setHistory(2009, 2010);
|
||||
}
|
||||
|
||||
function writeServerStaff_BubblesDEV() {
|
||||
addPerson("David!", "Developer");
|
||||
addPerson("Moogra", "Developer");
|
||||
addPerson("XxOsirisxX", "Contributor");
|
||||
addPerson("MrMysterious", "Contributor");
|
||||
|
||||
setHistory(2009, 2010);
|
||||
}
|
||||
|
||||
function writeServerStaff_MetroMS() {
|
||||
addPerson("David!", "Developer");
|
||||
addPerson("XxOsirisxX", "Contributor");
|
||||
addPerson("Generic", "Contributor");
|
||||
|
||||
setHistory(2009, 2009);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ function enter(pi) {
|
||||
if (pi.getPlayer().getMap().getMonsterById(9300216) != null) {
|
||||
pi.getPlayer().enteredScript("dojang_Msg", pi.getPlayer().getMap().getId());
|
||||
pi.getPlayer().setFinishedDojoTutorial();
|
||||
pi.getClient().getChannelServer().resetDojo(pi.getPlayer().getMap().getId());
|
||||
pi.getClient().getChannelServer().dismissDojoSchedule(pi.getPlayer().getMap().getId(), pi.getParty());
|
||||
pi.playPortalSound(); pi.warp(925020001, 0);
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
* @maps: All Dojo fighting maps
|
||||
*/
|
||||
|
||||
importPackage(Packages.constants.game);
|
||||
|
||||
function enter(pi) {
|
||||
try {
|
||||
@@ -32,7 +33,7 @@ function enter(pi) {
|
||||
pi.goDojoUp();
|
||||
pi.getPlayer().getMap().setReactorState();
|
||||
var stage = Math.floor(pi.getPlayer().getMapId() / 100) % 100;
|
||||
if ((stage - (stage / 6) | 0) == pi.getPlayer().getVanquisherStage() && !pi.getPlayer().getDojoParty()) // we can also try 5 * stage / 6 | 0 + 1
|
||||
if ((stage - (stage / 6) | 0) == pi.getPlayer().getVanquisherStage() && !GameConstants.isDojoPartyArea(pi.getPlayer().getMapId())) // we can also try 5 * stage / 6 | 0 + 1
|
||||
pi.getPlayer().setVanquisherKills(pi.getPlayer().getVanquisherKills() + 1);
|
||||
} else {
|
||||
pi.getPlayer().message("There are still some monsters remaining.");
|
||||
|
||||
@@ -25,15 +25,8 @@
|
||||
*/
|
||||
function enter(pi) {
|
||||
if(pi.isQuestStarted(6110)) {
|
||||
if(pi.getWarpMap(910500100).countPlayers() == 0) {
|
||||
pi.resetMapObjects(910500100);
|
||||
pi.playPortalSound(); pi.warp(910500100, 0);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
pi.getPlayer().message("Some other player is currently inside.");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
pi.getPlayer().message("A mysterious force won't let you in.");
|
||||
return false;
|
||||
|
||||
+14
-18
@@ -6013,7 +6013,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(7130000, 1072210, 1, 1, 0, 800),
|
||||
(7130000, 1002275, 1, 1, 0, 1500),
|
||||
(7130000, 1072177, 1, 1, 0, 800),
|
||||
(7130000, 4161021, 1, 1, 0, 6000),
|
||||
(7130000, 4161021, 1, 1, 0, 7000),
|
||||
(7130000, 1072312, 1, 1, 0, 800),
|
||||
(7130000, 2044901, 1, 1, 0, 300),
|
||||
(7130000, 2040419, 1, 1, 0, 300),
|
||||
@@ -7120,7 +7120,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8140102, 1452019, 1, 1, 0, 500),
|
||||
(8140102, 1382035, 1, 1, 0, 700),
|
||||
(8140102, 1432004, 1, 1, 0, 500),
|
||||
(8140102, 4161015, 1, 1, 0, 6000),
|
||||
(8140102, 4161015, 1, 1, 0, 7000),
|
||||
(8140102, 1002643, 1, 1, 0, 1500),
|
||||
(8140102, 2331000, 1, 1, 0, 500),
|
||||
(8140102, 2040321, 1, 1, 0, 300),
|
||||
@@ -7152,7 +7152,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8140103, 1322045, 1, 1, 0, 700),
|
||||
(8140103, 1412021, 1, 1, 0, 700),
|
||||
(8140103, 1432011, 1, 1, 0, 500),
|
||||
(8140103, 4161016, 1, 1, 0, 6000),
|
||||
(8140103, 4161016, 1, 1, 0, 7000),
|
||||
(8140103, 1492010, 1, 1, 0, 500),
|
||||
(8140103, 2332000, 1, 1, 0, 500),
|
||||
(8140103, 2044314, 1, 1, 0, 300),
|
||||
@@ -7713,7 +7713,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8150100, 2041013, 1, 1, 0, 300),
|
||||
(8150100, 1002366, 1, 1, 0, 1500),
|
||||
(8150100, 1072214, 1, 1, 0, 800),
|
||||
(8150100, 4161018, 1, 1, 0, 6000),
|
||||
(8150100, 4161018, 1, 1, 0, 7000),
|
||||
(8150100, 1072315, 1, 1, 0, 800),
|
||||
(8150100, 1052131, 1, 1, 0, 700),
|
||||
(8150100, 2044902, 1, 1, 0, 300),
|
||||
@@ -7745,7 +7745,7 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8150101, 1072223, 1, 1, 0, 800),
|
||||
(8150101, 2290042, 1, 1, 0, 500),
|
||||
(8150101, 2290052, 1, 1, 0, 500),
|
||||
(8150101, 4161018, 1, 1, 0, 6000),
|
||||
(8150101, 4161018, 1, 1, 0, 7000),
|
||||
(8150101, 1072318, 1, 1, 0, 800),
|
||||
(8150101, 2290102, 1, 1, 0, 500),
|
||||
(8150101, 2040420, 1, 1, 0, 300),
|
||||
@@ -10287,10 +10287,6 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8190000, 0, 800, 1200, 0, 400000),
|
||||
(8190002, 0, 900, 1300, 0, 400000),
|
||||
(9400545, 0, 600, 900, 0, 400000),
|
||||
(9001000, 0, 200, 400, 0, 400000),
|
||||
(9001001, 0, 200, 400, 0, 400000),
|
||||
(9001002, 0, 200, 400, 0, 400000),
|
||||
(9001003, 0, 200, 400, 0, 400000),
|
||||
(9420500, 0, 36, 54, 0, 400000),
|
||||
(9420502, 0, 30, 42, 0, 400000),
|
||||
(9420506, 0, 56, 69, 0, 400000),
|
||||
@@ -10735,14 +10731,14 @@ INSERT IGNORE INTO `temp_data` (`dropperid`, `itemid`, `minimum_quantity`, `maxi
|
||||
(8500002, 4031869, 1, 1, 6360, 999999),
|
||||
(8141000, 4031873, 1, 1, 6380, 60000),
|
||||
(8141100, 4031874, 1, 1, 6390, 60000),
|
||||
(7130101, 4001112, 1, 1, 0, 1000),
|
||||
(8170000, 4001112, 1, 1, 0, 1500),
|
||||
(5130107, 4001107, 1, 1, 0, 1000),
|
||||
(8143000, 4001107, 1, 1, 0, 1500),
|
||||
(7160000, 4001110, 1, 1, 0, 1000),
|
||||
(8150100, 4161018, 1, 1, 0, 1000),
|
||||
(7130000, 4161021, 1, 1, 0, 1000),
|
||||
(8150000, 4001111, 1, 1, 0, 999999),
|
||||
(7130101, 4001112, 1, 1, 0, 7000),
|
||||
(8170000, 4001112, 1, 1, 0, 7500),
|
||||
(5130107, 4001107, 1, 1, 0, 7000),
|
||||
(8143000, 4001107, 1, 1, 0, 7500),
|
||||
(7160000, 4001110, 1, 1, 0, 7000),
|
||||
(8150100, 4161018, 1, 1, 0, 7000),
|
||||
(7130000, 4161021, 1, 1, 0, 7000),
|
||||
(8150000, 4001111, 1, 1, 0, 700000),
|
||||
(8140000, 4031477, 1, 1, 0, 10000),
|
||||
(8170000, 4031453, 1, 1, 6291, 50000),
|
||||
(8160000, 4031474, 1, 1, 6295, 50000),
|
||||
@@ -20207,7 +20203,7 @@ INSERT INTO `shopitems` (`shopitemid`, `shopid`, `itemid`, `price`, `pitch`, `po
|
||||
(2755, 9201059, 1312005, 42500, 0, 156),
|
||||
(2756, 9201059, 1302068, 352500, 0, 160),
|
||||
(2757, 9201059, 1302008, 42500, 0, 164),
|
||||
(2758, 9201060, 2010003, 100, 0, 104),
|
||||
(2758, 9201060, 2010003, 100, 0, 178),
|
||||
(2759, 9201060, 2061000, 1, 0, 108),
|
||||
(2760, 9201060, 2060000, 1, 0, 112),
|
||||
(2761, 9201060, 2030000, 400, 0, 116),
|
||||
|
||||
+3
-7
@@ -20278,8 +20278,8 @@ USE `heavenms`;
|
||||
(4300008, 4032521, 1, 1, 2291, 120000),
|
||||
(4300009, 4032521, 1, 1, 2291, 120000),
|
||||
(4300010, 4032521, 1, 1, 2291, 120000),
|
||||
(5130107, 4001207, 1, 1, 0, 2285),
|
||||
(5130108, 4001207, 1, 1, 0, 2285),
|
||||
(5130107, 4001207, 1, 1, 20402, 100000),
|
||||
(5130108, 4001207, 1, 1, 20402, 100000),
|
||||
(9400506, 2020020, 1, 1, 0, 100000),
|
||||
(9400506, 4031217, 1, 1, 0, 40000),
|
||||
(9400507, 2020020, 1, 1, 0, 100000),
|
||||
@@ -22844,8 +22844,6 @@ DELETE FROM temp_data WHERE dropperid >= 9300315 AND dropperid <= 9300324;
|
||||
(8220009, 0, 1479, 7280, 0, 400000),
|
||||
(8830000, 0, 2400, 11620, 0, 400000),
|
||||
(8830007, 0, 2400, 11620, 0, 400000),
|
||||
(9001009, 0, 1254, 6170, 0, 400000),
|
||||
(9001011, 0, 95, 140, 0, 400000),
|
||||
(9200016, 0, 81, 119, 0, 400000),
|
||||
(9200019, 0, 203, 299, 0, 400000),
|
||||
(9300011, 0, 109, 160, 0, 400000),
|
||||
@@ -22959,9 +22957,7 @@ DELETE FROM temp_data WHERE dropperid >= 9300315 AND dropperid <= 9300324;
|
||||
(9300269, 0, 174, 850, 0, 400000),
|
||||
(9300270, 0, 418, 617, 0, 400000),
|
||||
(9300274, 0, 39, 57, 0, 400000),
|
||||
(9300289, 0, 1704, 8530, 0, 400000),
|
||||
(9300294, 0, 2142, 10490, 0, 400000),
|
||||
(9300315, 0, 483, 2370, 0, 400000),
|
||||
(9300315, 0, 483, 2370, 0, 400000), # thanks Vcoc for noticing some Cygnus questline bosses dropping mesos
|
||||
(9300316, 0, 516, 2540, 0, 400000),
|
||||
(9300317, 0, 552, 2710, 0, 400000),
|
||||
(9300318, 0, 588, 2890, 0, 400000),
|
||||
|
||||
@@ -144,6 +144,7 @@ import client.processor.action.PetAutopotProcessor;
|
||||
import constants.game.ExpTable;
|
||||
import constants.game.GameConstants;
|
||||
import constants.inventory.ItemConstants;
|
||||
import constants.net.ServerConstants;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Beginner;
|
||||
import constants.skills.Bishop;
|
||||
@@ -279,8 +280,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private Map<Integer, MapleSummon> summons = new LinkedHashMap<>();
|
||||
private Map<Integer, MapleCoolDownValueHolder> coolDowns = new LinkedHashMap<>();
|
||||
private EnumMap<MapleDisease, Pair<MapleDiseaseValueHolder, MobSkill>> diseases = new EnumMap<>(MapleDisease.class);
|
||||
public byte[] m_aQuickslotLoaded;
|
||||
public MapleQuickslotBinding m_pQuickslotKeyMapped;
|
||||
private byte[] m_aQuickslotLoaded;
|
||||
private MapleQuickslotBinding m_pQuickslotKeyMapped;
|
||||
private MapleDoor pdoor = null;
|
||||
private Map<MapleQuest, Long> questExpirations = new LinkedHashMap<>();
|
||||
private ScheduledFuture<?> dragonBloodSchedule;
|
||||
@@ -678,7 +679,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
int pts = 0;
|
||||
if (dojoPoints < 17000) {
|
||||
pts = 1 + ((mapid - 1) / 100 % 100) / 6;
|
||||
if (!getDojoParty()) {
|
||||
if (!GameConstants.isDojoPartyArea(this.getMapId())) {
|
||||
pts++;
|
||||
}
|
||||
this.dojoPoints += pts;
|
||||
@@ -1322,6 +1323,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void changeQuickslotKeybinding(byte[] aQuickslotKeyMapped) {
|
||||
this.m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped);
|
||||
}
|
||||
|
||||
public void broadcastStance(int newStance) {
|
||||
setStance(newStance);
|
||||
broadcastStance();
|
||||
@@ -1820,6 +1825,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.MAPLE_MAP, "Character " + this.getName() + " got stuck when moving to map " + map.getId() + ".");
|
||||
client.disconnect(true, false); // thanks BHB for noticing a player storage stuck case here
|
||||
return;
|
||||
}
|
||||
|
||||
notifyMapTransferToPartner(map.getId());
|
||||
@@ -2804,9 +2811,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
dispelDebuff(MapleDisease.POISON);
|
||||
dispelDebuff(MapleDisease.SEAL);
|
||||
dispelDebuff(MapleDisease.WEAKEN);
|
||||
dispelDebuff(MapleDisease.SLOW);
|
||||
dispelDebuff(MapleDisease.ZOMBIFY);
|
||||
dispelDebuff(MapleDisease.CONFUSE);
|
||||
dispelDebuff(MapleDisease.SLOW); // thanks Conrad for noticing ZOMBIFY isn't dispellable
|
||||
}
|
||||
|
||||
public void cancelAllDebuffs() {
|
||||
@@ -4772,10 +4777,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return dojoEnergy;
|
||||
}
|
||||
|
||||
public boolean getDojoParty() {
|
||||
return mapid >= 925030100 && mapid < 925040000;
|
||||
}
|
||||
|
||||
public int getDojoPoints() {
|
||||
return dojoPoints;
|
||||
}
|
||||
@@ -5323,6 +5324,14 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
return partnerId > 0 ? marriageRing : null;
|
||||
}
|
||||
|
||||
public int getMasterLevel(int skill) {
|
||||
SkillEntry ret = skills.get(SkillFactory.getSkill(skill));
|
||||
if (ret == null) {
|
||||
return 0;
|
||||
}
|
||||
return ret.masterlevel;
|
||||
}
|
||||
|
||||
public int getMasterLevel(Skill skill) {
|
||||
if (skills.get(skill) == null) {
|
||||
return 0;
|
||||
@@ -8351,9 +8360,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
ps.close();
|
||||
|
||||
// No quickslots, or no change.
|
||||
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded));
|
||||
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.GetKeybindings(), this.m_aQuickslotLoaded));
|
||||
if (!bQuickslotEquals) {
|
||||
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped);
|
||||
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.GetKeybindings());
|
||||
|
||||
try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
|
||||
pInsertStatement.setInt(1, this.getAccountID());
|
||||
@@ -8616,9 +8625,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
ps.close();
|
||||
|
||||
// No quickslots, or no change.
|
||||
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped, this.m_aQuickslotLoaded));
|
||||
boolean bQuickslotEquals = this.m_pQuickslotKeyMapped == null || (this.m_aQuickslotLoaded != null && Arrays.equals(this.m_pQuickslotKeyMapped.GetKeybindings(), this.m_aQuickslotLoaded));
|
||||
if (!bQuickslotEquals) {
|
||||
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.m_aQuickslotKeyMapped);
|
||||
long nQuickslotKeymapped = LongTool.BytesToLong(this.m_pQuickslotKeyMapped.GetKeybindings());
|
||||
|
||||
try (final PreparedStatement pInsertStatement = con.prepareStatement("INSERT INTO quickslotkeymapped (accountid, keymap) VALUES (?, ?) ON DUPLICATE KEY UPDATE keymap = ?;")) {
|
||||
pInsertStatement.setInt(1, this.getAccountID());
|
||||
@@ -9214,9 +9223,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
MapleKeyBinding autohpPot = this.getKeymap().get(91);
|
||||
if (autohpPot != null) {
|
||||
int autohpItemid = autohpPot.getAction();
|
||||
if (((float) this.getHp()) / this.getCurrentMaxHp() <= this.getAutopotHpAlert()) { // try within user settings... thanks Lame, Optimist, Stealth2800
|
||||
float autohpAlert = this.getAutopotHpAlert();
|
||||
if (((float) this.getHp()) / this.getCurrentMaxHp() <= autohpAlert) { // try within user settings... thanks Lame, Optimist, Stealth2800
|
||||
Item autohpItem = this.getInventory(MapleInventoryType.USE).findById(autohpItemid);
|
||||
if (autohpItem != null) {
|
||||
this.setAutopotHpAlert(0.9f * autohpAlert);
|
||||
PetAutopotProcessor.runAutopotAction(client, autohpItem.getPosition(), autohpItemid);
|
||||
}
|
||||
}
|
||||
@@ -9227,9 +9238,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
MapleKeyBinding autompPot = this.getKeymap().get(92);
|
||||
if (autompPot != null) {
|
||||
int autompItemid = autompPot.getAction();
|
||||
if (((float) this.getMp()) / this.getCurrentMaxMp() <= this.getAutopotMpAlert()) {
|
||||
float autompAlert = this.getAutopotMpAlert();
|
||||
if (((float) this.getMp()) / this.getCurrentMaxMp() <= autompAlert) {
|
||||
Item autompItem = this.getInventory(MapleInventoryType.USE).findById(autompItemid);
|
||||
if (autompItem != null) {
|
||||
this.setAutopotMpAlert(0.9f * autompAlert); // autoMP would stick to using pots at every depletion in some cases... thanks Rohenn
|
||||
PetAutopotProcessor.runAutopotAction(client, autompItem.getPosition(), autompItemid);
|
||||
}
|
||||
}
|
||||
@@ -9689,17 +9702,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public void showDojoClock() {
|
||||
if (map.isDojoFightMap()) {
|
||||
if (GameConstants.isDojoBossArea(map.getId())) {
|
||||
client.announce(MaplePacketCreator.getClock((int) (getDojoTimeLeft() / 1000)));
|
||||
}
|
||||
}
|
||||
|
||||
public void timeoutFromDojo() {
|
||||
if(map.isDojoMap()) {
|
||||
client.getPlayer().changeMap(client.getChannelServer().getMapFactory().getMap(925020002));
|
||||
}
|
||||
}
|
||||
|
||||
public void showUnderleveledInfo(MapleMonster mob) {
|
||||
long curTime = Server.getInstance().getCurrentTime();
|
||||
if(nextWarningTime < curTime) {
|
||||
|
||||
@@ -25,12 +25,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
@@ -448,6 +450,20 @@ public class MapleInventory implements Iterable<Item> {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkItemRestricted(List<Pair<Item, MapleInventoryType>> items) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
Set<Integer> itemids = new HashSet<>();
|
||||
for (Pair<Item, MapleInventoryType> p : items) {
|
||||
int itemid = p.getLeft().getItemId();
|
||||
if (ii.isPickupRestricted(itemid) && (p.getLeft().getQuantity() > 1 || !itemids.add(itemid))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkSpot(MapleCharacter chr, Item item) { // thanks Vcoc for noticing pshops not checking item stacks when taking item back
|
||||
return checkSpot(chr, Collections.singletonList(item));
|
||||
}
|
||||
@@ -476,6 +492,10 @@ public class MapleInventory implements Iterable<Item> {
|
||||
public static boolean checkSpots(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, List<Integer> typesSlotsUsed, boolean useProofInv) {
|
||||
// assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0.
|
||||
|
||||
if (!checkItemRestricted(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> rcvItems = new LinkedHashMap<>();
|
||||
Map<Integer, Byte> rcvTypes = new LinkedHashMap<>();
|
||||
|
||||
@@ -490,7 +510,7 @@ public class MapleInventory implements Iterable<Item> {
|
||||
rcvItems.put(itemId, itemQtyList);
|
||||
rcvTypes.put(itemId, item.right.getType());
|
||||
} else {
|
||||
if (!ItemConstants.isRechargeable(itemId)) {
|
||||
if (!ItemConstants.isEquipment(itemId) && !ItemConstants.isRechargeable(itemId)) {
|
||||
qty.set(0, qty.get(0) + item.left.getQuantity());
|
||||
} else {
|
||||
qty.add((int) item.left.getQuantity());
|
||||
@@ -548,6 +568,10 @@ public class MapleInventory implements Iterable<Item> {
|
||||
public static boolean checkSpotsAndOwnership(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, List<Integer> typesSlotsUsed, boolean useProofInv) {
|
||||
//assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0 and item list to be checked is a legal one.
|
||||
|
||||
if (!checkItemRestricted(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Long, List<Integer>> rcvItems = new LinkedHashMap<>();
|
||||
Map<Long, Byte> rcvTypes = new LinkedHashMap<>();
|
||||
Map<Long, String> rcvOwners = new LinkedHashMap<>();
|
||||
@@ -565,7 +589,7 @@ public class MapleInventory implements Iterable<Item> {
|
||||
rcvOwners.put(itemHash, item.left.getOwner());
|
||||
} else {
|
||||
// thanks BHB88 for pointing out an issue with rechargeable items being stacked on inventory check
|
||||
if (!ItemConstants.isRechargeable(item.left.getItemId())) {
|
||||
if (!ItemConstants.isEquipment(item.left.getItemId()) && !ItemConstants.isRechargeable(item.left.getItemId())) {
|
||||
qty.set(0, qty.get(0) + item.left.getQuantity());
|
||||
} else {
|
||||
qty.add((int) item.left.getQuantity());
|
||||
|
||||
@@ -106,7 +106,7 @@ public class MapleInventoryManipulator {
|
||||
}
|
||||
}
|
||||
boolean sandboxItem = (flag & ItemConstants.SANDBOX) == ItemConstants.SANDBOX;
|
||||
while (quantity > 0 || ItemConstants.isRechargeable(itemId)) {
|
||||
while (quantity > 0) {
|
||||
short newQ = (short) Math.min(quantity, slotMax);
|
||||
if (newQ != 0) {
|
||||
quantity -= newQ;
|
||||
@@ -124,9 +124,6 @@ public class MapleInventoryManipulator {
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
|
||||
if(sandboxItem) chr.setHasSandboxItem();
|
||||
if ((ItemConstants.isRechargeable(itemId)) && quantity == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
@@ -189,8 +186,8 @@ public class MapleInventoryManipulator {
|
||||
|
||||
private static boolean addFromDropInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, Item item, boolean show, int petId) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
int itemid = item.getItemId();
|
||||
if (ii.isPickupRestricted(itemid) && chr.haveItemWithId(itemid, true)) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.showItemUnavailable());
|
||||
return false;
|
||||
@@ -198,9 +195,9 @@ public class MapleInventoryManipulator {
|
||||
short quantity = item.getQuantity();
|
||||
|
||||
if (!type.equals(MapleInventoryType.EQUIP)) {
|
||||
short slotMax = ii.getSlotMax(c, item.getItemId());
|
||||
List<Item> existing = inv.listById(item.getItemId());
|
||||
if (!ItemConstants.isRechargeable(item.getItemId()) && petId == -1) {
|
||||
short slotMax = ii.getSlotMax(c, itemid);
|
||||
List<Item> existing = inv.listById(itemid);
|
||||
if (!ItemConstants.isRechargeable(itemid) && petId == -1) {
|
||||
if (existing.size() > 0) { // first update all existing slots to slotMax
|
||||
Iterator<Item> i = existing.iterator();
|
||||
while (quantity > 0) {
|
||||
@@ -222,7 +219,7 @@ public class MapleInventoryManipulator {
|
||||
while (quantity > 0) {
|
||||
short newQ = (short) Math.min(quantity, slotMax);
|
||||
quantity -= newQ;
|
||||
Item nItem = new Item(item.getItemId(), (short) 0, newQ, petId);
|
||||
Item nItem = new Item(itemid, (short) 0, newQ, petId);
|
||||
nItem.setExpiration(item.getExpiration());
|
||||
nItem.setOwner(item.getOwner());
|
||||
nItem.setFlag(item.getFlag());
|
||||
@@ -239,7 +236,7 @@ public class MapleInventoryManipulator {
|
||||
if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
|
||||
}
|
||||
} else {
|
||||
Item nItem = new Item(item.getItemId(), (short) 0, quantity, petId);
|
||||
Item nItem = new Item(itemid, (short) 0, quantity, petId);
|
||||
nItem.setExpiration(item.getExpiration());
|
||||
nItem.setFlag(item.getFlag());
|
||||
|
||||
@@ -266,13 +263,13 @@ public class MapleInventoryManipulator {
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item))));
|
||||
if (MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem();
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + item.getItemId() + " containing more than 1 quantity --> " + quantity);
|
||||
FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + itemid + " containing more than 1 quantity --> " + quantity);
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.showItemUnavailable());
|
||||
return false;
|
||||
}
|
||||
if (show) {
|
||||
c.announce(MaplePacketCreator.getShowItemGain(item.getItemId(), item.getQuantity()));
|
||||
c.announce(MaplePacketCreator.getShowItemGain(itemid, item.getQuantity()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class MapleQuickslotBinding
|
||||
0x2A, 0x52, 0x47, 0x49, 0x1D, 0x53, 0x4F, 0x51
|
||||
};
|
||||
|
||||
public byte[] m_aQuickslotKeyMapped;
|
||||
private byte[] m_aQuickslotKeyMapped;
|
||||
|
||||
// Initializes quickslot object for the user.
|
||||
// aKeys' length has to be 8.
|
||||
@@ -52,4 +52,9 @@ public class MapleQuickslotBinding
|
||||
oPacket.writeInt(nKey);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] GetKeybindings() {
|
||||
return m_aQuickslotKeyMapped;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -308,7 +308,7 @@ public class MakerProcessor {
|
||||
}
|
||||
|
||||
private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) {
|
||||
if(recipe == null) {
|
||||
if(recipe.isInvalid()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -585,6 +585,15 @@ public class GameConstants {
|
||||
return mapid >= 925020000 && mapid < 925040000;
|
||||
}
|
||||
|
||||
public static boolean isDojoPartyArea(int mapid) {
|
||||
return mapid >= 925030100 && mapid < 925040000;
|
||||
}
|
||||
|
||||
public static boolean isDojoBossArea(int mapid) {
|
||||
return isDojo(mapid) && (((mapid / 100) % 100) % 6) > 0;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isPyramid(int mapid) {
|
||||
return mapid >= 926010010 & mapid <= 930010000;
|
||||
}
|
||||
|
||||
+65
-34
@@ -320,7 +320,7 @@ public class Server {
|
||||
|
||||
|
||||
private void dumpData() {
|
||||
wldWLock.lock();
|
||||
wldRLock.lock();
|
||||
try {
|
||||
System.out.println(worlds);
|
||||
System.out.println(channels);
|
||||
@@ -328,36 +328,46 @@ public class Server {
|
||||
System.out.println();
|
||||
System.out.println("---------------------");
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
wldRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int addChannel(int worldid) {
|
||||
wldWLock.lock();
|
||||
World world;
|
||||
Map<Integer, String> channelInfo;
|
||||
int channelid;
|
||||
|
||||
wldRLock.lock();
|
||||
try {
|
||||
if(worldid >= worlds.size()) return -3;
|
||||
|
||||
Map<Integer, String> worldChannels = channels.get(worldid);
|
||||
if(worldChannels == null) return -3;
|
||||
channelInfo = channels.get(worldid);
|
||||
if(channelInfo == null) return -3;
|
||||
|
||||
int channelid = worldChannels.size();
|
||||
channelid = channelInfo.size();
|
||||
if(channelid >= YamlConfig.config.server.CHANNEL_SIZE) return -2;
|
||||
|
||||
channelid++;
|
||||
World world = this.getWorld(worldid);
|
||||
Channel channel = new Channel(worldid, channelid, getCurrentTime());
|
||||
world = this.getWorld(worldid);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
|
||||
Channel channel = new Channel(worldid, channelid, getCurrentTime());
|
||||
channel.setServerMessage(YamlConfig.config.worlds.get(worldid).why_am_i_recommended);
|
||||
|
||||
world.addChannel(channel);
|
||||
worldChannels.put(channelid, channel.getIP());
|
||||
|
||||
return channelid;
|
||||
if (world.addChannel(channel)) {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
channelInfo.put(channelid, channel.getIP());
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
return channelid;
|
||||
}
|
||||
|
||||
public int addWorld() {
|
||||
int newWorld = initWorld();
|
||||
if(newWorld > -1) {
|
||||
@@ -380,13 +390,18 @@ public class Server {
|
||||
}
|
||||
|
||||
private int initWorld() {
|
||||
wldWLock.lock();
|
||||
int i;
|
||||
|
||||
wldRLock.lock();
|
||||
try {
|
||||
int i = worlds.size();
|
||||
i = worlds.size();
|
||||
|
||||
if(i >= YamlConfig.config.server.WLDLIST_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
|
||||
System.out.println("Starting world " + i);
|
||||
|
||||
@@ -407,9 +422,6 @@ public class Server {
|
||||
event_message,
|
||||
exprate, droprate, bossdroprate, mesorate, questrate, travelrate, fishingrate);
|
||||
|
||||
worldRecommendedList.add(new Pair<>(i, why_am_i_recommended));
|
||||
worlds.add(world);
|
||||
|
||||
Map<Integer, String> channelInfo = new HashMap<>();
|
||||
long bootTime = getCurrentTime();
|
||||
for (int j = 1; j <= YamlConfig.config.worlds.get(i).channels; j++) {
|
||||
@@ -420,35 +432,56 @@ public class Server {
|
||||
channelInfo.put(channelid, channel.getIP());
|
||||
}
|
||||
|
||||
boolean canDeploy;
|
||||
|
||||
wldWLock.lock(); // thanks Ashen for noticing a deadlock issue when trying to deploy a channel
|
||||
try {
|
||||
canDeploy = world.getId() == worlds.size();
|
||||
if (canDeploy) {
|
||||
worldRecommendedList.add(new Pair<>(i, why_am_i_recommended));
|
||||
worlds.add(world);
|
||||
channels.add(i, channelInfo);
|
||||
|
||||
world.setServerMessage(YamlConfig.config.worlds.get(i).server_message);
|
||||
System.out.println("Finished loading world " + i + "\r\n");
|
||||
|
||||
return i;
|
||||
}
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
if (canDeploy) {
|
||||
world.setServerMessage(YamlConfig.config.worlds.get(i).server_message);
|
||||
|
||||
System.out.println("Finished loading world " + i + "\r\n");
|
||||
return i;
|
||||
} else {
|
||||
System.out.println("Could not load world " + i + "...\r\n");
|
||||
world.shutdown();
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean removeChannel(int worldid) { //lol don't!
|
||||
wldWLock.lock();
|
||||
World world;
|
||||
|
||||
wldRLock.lock();
|
||||
try {
|
||||
if(worldid >= worlds.size()) return false;
|
||||
world = worlds.get(worldid);
|
||||
} finally {
|
||||
wldRLock.unlock();
|
||||
}
|
||||
|
||||
World world = worlds.get(worldid);
|
||||
if (world != null) {
|
||||
int channel = world.removeChannel();
|
||||
|
||||
wldWLock.lock();
|
||||
try {
|
||||
Map<Integer, String> m = channels.get(worldid);
|
||||
if(m != null) m.remove(channel);
|
||||
|
||||
return channel > -1;
|
||||
}
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
return channel > -1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -472,17 +505,15 @@ public class Server {
|
||||
return false;
|
||||
}
|
||||
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if(worldid == worlds.size() - 1) {
|
||||
removeWorldPlayerRanking();
|
||||
w.shutdown();
|
||||
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if (worldid == worlds.size() - 1) {
|
||||
worlds.remove(worldid);
|
||||
channels.remove(worldid);
|
||||
worldRecommendedList.remove(worldid);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
@@ -714,7 +745,7 @@ public class Server {
|
||||
if (!YamlConfig.config.server.USE_WHOLE_SERVER_RANKING) {
|
||||
wldWLock.lock();
|
||||
try {
|
||||
if(playerRanking.size() < this.getWorldsSize()) {
|
||||
if(playerRanking.size() < worlds.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ import org.apache.mina.transport.socket.SocketSessionConfig;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import constants.game.GameConstants;
|
||||
import net.server.services.ServicesManager;
|
||||
import net.server.services.BaseService;
|
||||
import net.server.services.type.ChannelServices;
|
||||
@@ -97,18 +98,18 @@ public final class Channel {
|
||||
private final Map<Integer, Integer> storedVars = new HashMap<>();
|
||||
private Set<Integer> playersAway = new HashSet<>();
|
||||
private Map<MapleExpeditionType, MapleExpedition> expeditions = new HashMap<>();
|
||||
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
|
||||
private List<MapleExpeditionType> expedType = new ArrayList<>();
|
||||
private Set<MapleMap> ownedMaps = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<MapleMap, Boolean>()));
|
||||
private MapleEvent event;
|
||||
private boolean finishedShutdown = false;
|
||||
private int usedDojo = 0;
|
||||
private Set<Integer> usedMC = new HashSet<>();
|
||||
|
||||
private int usedDojo = 0;
|
||||
private int[] dojoStage;
|
||||
private long[] dojoFinishTime;
|
||||
private ScheduledFuture<?>[] dojoTask;
|
||||
private Map<Integer, Integer> dojoParty = new HashMap<>();
|
||||
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
|
||||
|
||||
private List<Integer> chapelReservationQueue = new LinkedList<>();
|
||||
private List<Integer> cathedralReservationQueue = new LinkedList<>();
|
||||
@@ -226,12 +227,17 @@ public final class Channel {
|
||||
}
|
||||
|
||||
private void closeChannelSchedules() {
|
||||
for(int i = 0; i < 20; i++) {
|
||||
lock.lock();
|
||||
try {
|
||||
for(int i = 0; i < dojoTask.length; i++) {
|
||||
if(dojoTask[i] != null) {
|
||||
dojoTask[i].cancel(false);
|
||||
dojoTask[i] = null;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
closeChannelServices();
|
||||
disposeLocks();
|
||||
@@ -493,18 +499,20 @@ public final class Channel {
|
||||
this.storedVars.put(key, val);
|
||||
}
|
||||
|
||||
public synchronized int lookupPartyDojo(MapleParty party) {
|
||||
public int lookupPartyDojo(MapleParty party) {
|
||||
if(party == null) return -1;
|
||||
|
||||
Integer i = dojoParty.get(party.hashCode());
|
||||
return (i != null) ? i : -1;
|
||||
}
|
||||
|
||||
public int getAvailableDojo(boolean isPartyDojo) {
|
||||
return getAvailableDojo(isPartyDojo, null);
|
||||
public int ingressDojo(boolean isPartyDojo, int fromStage) {
|
||||
return ingressDojo(isPartyDojo, null, fromStage);
|
||||
}
|
||||
|
||||
public synchronized int getAvailableDojo(boolean isPartyDojo, MapleParty party) {
|
||||
public int ingressDojo(boolean isPartyDojo, MapleParty party, int fromStage) {
|
||||
lock.lock();
|
||||
try {
|
||||
int dojoList = this.usedDojo;
|
||||
int range, slot = 0;
|
||||
|
||||
@@ -521,29 +529,44 @@ public final class Channel {
|
||||
}
|
||||
|
||||
if(slot < range) {
|
||||
int slotMapid = (isPartyDojo ? 925030000 : 925020000) + (100 * (fromStage + 1)) + slot;
|
||||
int dojoSlot = getDojoSlot(slotMapid);
|
||||
|
||||
if(party != null) {
|
||||
if(dojoParty.containsKey(party.hashCode())) return -2;
|
||||
dojoParty.put(party.hashCode(), slot);
|
||||
dojoParty.put(party.hashCode(), dojoSlot);
|
||||
}
|
||||
|
||||
this.usedDojo |= (1 << ((!isPartyDojo ? 5 : 0) + slot));
|
||||
this.usedDojo |= (1 << dojoSlot);
|
||||
|
||||
this.resetDojo(slotMapid);
|
||||
this.startDojoSchedule(slotMapid);
|
||||
return slot;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void freeDojoSlot(int slot, MapleParty party) {
|
||||
int mask = 0b11111111111111111111;
|
||||
mask ^= (1 << slot);
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
usedDojo &= mask;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if(party != null) {
|
||||
if(dojoParty.remove(party.hashCode()) != null) return;
|
||||
}
|
||||
|
||||
if(dojoParty.containsValue(slot)) { // strange case, no party there!
|
||||
Set<Entry<Integer, Integer>> es = Collections.unmodifiableSet(dojoParty.entrySet());
|
||||
Set<Entry<Integer, Integer>> es = new HashSet<>(dojoParty.entrySet());
|
||||
|
||||
for(Entry<Integer, Integer> e: es) {
|
||||
if(e.getValue() == slot) {
|
||||
@@ -565,17 +588,12 @@ public final class Channel {
|
||||
}
|
||||
|
||||
public void resetDojo(int dojoMapId) {
|
||||
resetDojo(dojoMapId, 0);
|
||||
resetDojo(dojoMapId, -1);
|
||||
}
|
||||
|
||||
public void resetDojo(int dojoMapId, int thisStg) {
|
||||
private void resetDojo(int dojoMapId, int thisStg) {
|
||||
int slot = getDojoSlot(dojoMapId);
|
||||
this.dojoStage[slot] = thisStg;
|
||||
|
||||
if(this.dojoTask[slot] != null) {
|
||||
this.dojoTask[slot].cancel(false);
|
||||
this.dojoTask[slot] = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void freeDojoSectionIfEmpty(int dojoMapId) {
|
||||
@@ -595,12 +613,18 @@ public final class Channel {
|
||||
freeDojoSlot(slot, null);
|
||||
}
|
||||
|
||||
public void startDojoSchedule(final int dojoMapId) {
|
||||
private void startDojoSchedule(final int dojoMapId) {
|
||||
final int slot = getDojoSlot(dojoMapId);
|
||||
final int stage = (dojoMapId / 100) % 100;
|
||||
if(stage <= dojoStage[slot]) return;
|
||||
|
||||
long clockTime = (stage > 36 ? 15 : (stage / 6) + 5) * 60000;
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
if (this.dojoTask[slot] != null) {
|
||||
this.dojoTask[slot].cancel(false);
|
||||
}
|
||||
this.dojoTask[slot] = TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -612,9 +636,11 @@ public final class Channel {
|
||||
if (stage + i > 38) {
|
||||
break;
|
||||
}
|
||||
|
||||
MapleMap dojoExit = getMapFactory().getMap(925020002);
|
||||
for(MapleCharacter chr: getMapFactory().getMap(dojoBaseMap + (100 * (stage + i)) + delta).getAllPlayers()) {
|
||||
if(chr.getMap().isDojoMap()) {
|
||||
chr.timeoutFromDojo();
|
||||
if(GameConstants.isDojo(chr.getMap().getId())) {
|
||||
chr.changeMap(dojoExit);
|
||||
}
|
||||
party = chr.getParty();
|
||||
}
|
||||
@@ -623,6 +649,9 @@ public final class Channel {
|
||||
freeDojoSlot(slot, party);
|
||||
}
|
||||
}, clockTime + 3000); // let the TIMES UP display for 3 seconds, then warp
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
dojoFinishTime[slot] = Server.getInstance().getCurrentTime() + clockTime;
|
||||
}
|
||||
@@ -632,10 +661,15 @@ public final class Channel {
|
||||
int stage = (dojoMapId / 100) % 100;
|
||||
if(stage <= dojoStage[slot]) return;
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
if(this.dojoTask[slot] != null) {
|
||||
this.dojoTask[slot].cancel(false);
|
||||
this.dojoTask[slot] = null;
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
freeDojoSlot(slot, party);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ import client.autoban.AutobanFactory;
|
||||
import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import constants.game.GameConstants;
|
||||
import constants.net.ServerConstants;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Assassin;
|
||||
import constants.skills.Bandit;
|
||||
@@ -102,6 +101,7 @@ import constants.skills.SuperGM;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.WhiteKnight;
|
||||
import constants.skills.WindArcher;
|
||||
import net.server.PlayerBuffValueHolder;
|
||||
import scripting.AbstractPlayerInteraction;
|
||||
|
||||
public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandler {
|
||||
@@ -166,14 +166,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
if (player.isAlive()) {
|
||||
if(attack.skill == Aran.BODY_PRESSURE || attack.skill == Marauder.ENERGY_CHARGE || attack.skill == ThunderBreaker.ENERGY_CHARGE) { // thanks IxianMace for noticing Energy Charge skills refreshing on touch, leading to misleading buff applies
|
||||
// prevent touch dmg skills refreshing
|
||||
} else if(attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == WindArcher.FINAL_ATTACK) {
|
||||
// prevent cygnus FA refreshing
|
||||
mobCount = 15;
|
||||
} else if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb
|
||||
attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y));
|
||||
} else {
|
||||
attackEffect.applyTo(player);
|
||||
|
||||
if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
|
||||
|| attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK
|
||||
|| attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
|
||||
if (attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD
|
||||
|| attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM
|
||||
|| attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) {
|
||||
|
||||
mobCount = 15;//:(
|
||||
} else if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
@@ -753,6 +756,17 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
calcDmgMax *= (100 + ceffect.getDamage()) / 100;
|
||||
}
|
||||
|
||||
int bonusDmgBuff = 100;
|
||||
for (PlayerBuffValueHolder pbvh : chr.getAllBuffs()) {
|
||||
int bonusDmg = pbvh.effect.getDamage() - 100;
|
||||
bonusDmgBuff += bonusDmg;
|
||||
}
|
||||
|
||||
if (bonusDmgBuff != 100) {
|
||||
float dmgBuff = bonusDmgBuff / 100.0f;
|
||||
calcDmgMax = (long) Math.ceil(calcDmgMax * dmgBuff);
|
||||
}
|
||||
|
||||
if(chr.getMapId() >= 914000000 && chr.getMapId() <= 914000500) {
|
||||
calcDmgMax += 80000; // Aran Tutorial.
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
|
||||
|
||||
if (chr.getDojoEnergy() < 10000 && (attack.skill == 1009 || attack.skill == 10001009 || attack.skill == 20001009)) // PE hacking or maybe just lagging
|
||||
return;
|
||||
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
|
||||
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
|
||||
chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK);
|
||||
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import config.YamlConfig;
|
||||
import constants.game.GameConstants;
|
||||
import constants.skills.Bishop;
|
||||
import constants.skills.Evan;
|
||||
import constants.skills.FPArchMage;
|
||||
@@ -56,7 +57,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
|
||||
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
|
||||
chr.setDojoEnergy(chr.getDojoEnergy() + + YamlConfig.config.server.DOJO_ENERGY_ATK);
|
||||
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
String remoteHwid;
|
||||
if (player == null) {
|
||||
remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
|
||||
remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session);
|
||||
if (remoteHwid == null) {
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
|
||||
@@ -29,6 +29,6 @@ public class QuickslotKeyMappedModifiedHandler extends AbstractMaplePacketHandle
|
||||
aQuickslotKeyMapped[i] = (byte) slea.readInt();
|
||||
}
|
||||
|
||||
c.getPlayer().m_pQuickslotKeyMapped = new MapleQuickslotBinding(aQuickslotKeyMapped);
|
||||
c.getPlayer().changeQuickslotKeybinding(aQuickslotKeyMapped);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MapleWeaponType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import config.YamlConfig;
|
||||
import constants.game.GameConstants;
|
||||
import constants.inventory.ItemConstants;
|
||||
import constants.skills.Aran;
|
||||
import constants.skills.Buccaneer;
|
||||
@@ -69,7 +70,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (chr.getMap().isDojoMap() && attack.numAttacked > 0) {
|
||||
if (GameConstants.isDojo(chr.getMap().getId()) && attack.numAttacked > 0) {
|
||||
chr.setDojoEnergy(chr.getDojoEnergy() + YamlConfig.config.server.DOJO_ENERGY_ATK);
|
||||
c.announce(MaplePacketCreator.getEnergy("energy", chr.getDojoEnergy()));
|
||||
}
|
||||
@@ -104,7 +105,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
}
|
||||
short slot = -1;
|
||||
int projectile = 0;
|
||||
byte bulletCount = 1;
|
||||
short bulletCount = 1;
|
||||
MapleStatEffect effect = null;
|
||||
if (attack.skill != 0) {
|
||||
effect = attack.getAttackEffect(chr, null);
|
||||
@@ -168,7 +169,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
boolean shadowClaw = chr.getBuffedValue(MapleBuffStat.SHADOW_CLAW) != null;
|
||||
if (projectile != 0) {
|
||||
if (!soulArrow && !shadowClaw && attack.skill != 11101004 && attack.skill != 15111007 && attack.skill != 14101006) {
|
||||
byte bulletConsume = bulletCount;
|
||||
short bulletConsume = bulletCount;
|
||||
|
||||
if (effect != null && effect.getBulletConsume() != 0) {
|
||||
bulletConsume = (byte) (effect.getBulletConsume() * (hasShadowPartner ? 2 : 1));
|
||||
|
||||
@@ -197,7 +197,7 @@ public final class TakeDamageHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
//in dojo player cannot use pot, so deadly attacks should be turned off as well
|
||||
if(is_deadly && chr.getMap().isDojoMap() && !YamlConfig.config.server.USE_DEADLY_DOJO) {
|
||||
if(is_deadly && GameConstants.isDojo(chr.getMap().getId()) && !YamlConfig.config.server.USE_DEADLY_DOJO) {
|
||||
damage = 0;
|
||||
mpattack = 0;
|
||||
}
|
||||
|
||||
@@ -319,10 +319,14 @@ public class MapleMatchCheckerCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
private void acceptMatchElement(MapleMatchCheckingElement mmce, int cid) {
|
||||
private boolean acceptMatchElement(MapleMatchCheckingElement mmce, int cid) {
|
||||
if (mmce.acceptEntry(cid)) {
|
||||
unpoolMatchPlayer(cid);
|
||||
disposeMatchElement(mmce);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +359,11 @@ public class MapleMatchCheckerCoordinator {
|
||||
mmce = null;
|
||||
} else {
|
||||
if (accept) {
|
||||
acceptMatchElement(mmce, cid);
|
||||
if (!acceptMatchElement(mmce, cid)) {
|
||||
mmce = null;
|
||||
}
|
||||
|
||||
break; // thanks Rohenn for noticing loop scenario here
|
||||
} else {
|
||||
denyMatchElement(mmce, cid);
|
||||
matchEntries.remove(cid);
|
||||
|
||||
@@ -547,6 +547,11 @@ public class MapleSessionCoordinator {
|
||||
// session.removeAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); No real need for removing String property on closed sessions
|
||||
}
|
||||
|
||||
public String pickLoginSessionHwid(IoSession session) {
|
||||
String remoteHost = getSessionRemoteAddress(session);
|
||||
return cachedHostHwids.remove(remoteHost); // thanks BHB, resinate for noticing players from same network not being able to login
|
||||
}
|
||||
|
||||
public String getGameSessionHwid(IoSession session) {
|
||||
String remoteHost = getSessionRemoteHost(session);
|
||||
return cachedHostHwids.get(remoteHost);
|
||||
|
||||
@@ -88,7 +88,8 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
|
||||
|
||||
slea.skip(6); // localhost masked the initial part with zeroes...
|
||||
byte[] hwidNibbles = slea.read(4);
|
||||
int loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
|
||||
String nibbleHwid = HexTool.toCompressedString(hwidNibbles);
|
||||
int loginok = c.login(login, pwd, nibbleHwid);
|
||||
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
@@ -112,7 +113,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
disposeSql(con, ps);
|
||||
loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
|
||||
loginok = c.login(login, pwd, nibbleHwid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -260,10 +260,15 @@ public class World {
|
||||
}
|
||||
}
|
||||
|
||||
public void addChannel(Channel channel) {
|
||||
public boolean addChannel(Channel channel) {
|
||||
chnWLock.lock();
|
||||
try {
|
||||
if (channel.getId() == channels.size() + 1) {
|
||||
channels.add(channel);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} finally {
|
||||
chnWLock.unlock();
|
||||
}
|
||||
|
||||
@@ -588,7 +588,10 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
}
|
||||
|
||||
public Object[] getAvailableSkillBooks() {
|
||||
return MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer()).toArray();
|
||||
List<Integer> ret = MapleItemInformationProvider.getInstance().usableSkillBooks(this.getPlayer());
|
||||
ret.addAll(MapleSkillbookInformationProvider.getInstance().getTeachableSkills(this.getPlayer()));
|
||||
|
||||
return ret.toArray();
|
||||
}
|
||||
|
||||
public Object[] getNamesWhoDropsItem(Integer itemId) {
|
||||
@@ -597,7 +600,19 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
|
||||
public String getSkillBookInfo(int itemid) {
|
||||
SkillBookEntry sbe = MapleSkillbookInformationProvider.getInstance().getSkillbookAvailability(itemid);
|
||||
return sbe != SkillBookEntry.UNAVAILABLE ? " Obtainable through #rquestline#k." : "";
|
||||
switch (sbe) {
|
||||
case UNAVAILABLE:
|
||||
return "";
|
||||
|
||||
case QUEST_BOOK:
|
||||
return " Obtainable through #rquestline#k (collecting book).";
|
||||
|
||||
case QUEST_REWARD:
|
||||
return " Obtainable through #rquestline#k (quest reward).";
|
||||
|
||||
default:
|
||||
return " Obtainable through #rquestline#k.";
|
||||
}
|
||||
}
|
||||
|
||||
// By Drago/Dragohe4rt CPQ + WED
|
||||
|
||||
@@ -38,8 +38,8 @@ public class MakerItemFactory {
|
||||
|
||||
public static MakerItemCreateEntry getItemCreateEntry(int toCreate, int stimulantid, Map<Integer, Short> reagentids) {
|
||||
MakerItemCreateEntry makerEntry = ii.getMakerItemEntry(toCreate);
|
||||
if(makerEntry == null) {
|
||||
return null;
|
||||
if(makerEntry.isInvalid()) {
|
||||
return makerEntry;
|
||||
}
|
||||
|
||||
// THEY DECIDED FOR SOME BIZARRE PATTERN ON THE FEE THING, ALMOST RANDOMIZED.
|
||||
@@ -206,5 +206,9 @@ public class MakerItemFactory {
|
||||
reqCost = (int) (cost / 1000);
|
||||
reqCost *= 1000;
|
||||
}
|
||||
|
||||
public boolean isInvalid() { // thanks Rohenn, Wh1SK3Y for noticing some items not getting checked properly
|
||||
return reqLevel < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1981,10 +1981,10 @@ public class MapleItemInformationProvider {
|
||||
PreparedStatement ps = con.prepareStatement("SELECT req_level, req_maker_level, req_meso, quantity FROM makercreatedata WHERE itemid = ?");
|
||||
ps.setInt(1, toCreate);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
int reqLevel = 0;
|
||||
int reqMakerLevel = 0;
|
||||
int cost = 0;
|
||||
int toGive = 0;
|
||||
int reqLevel = -1;
|
||||
int reqMakerLevel = -1;
|
||||
int cost = -1;
|
||||
int toGive = -1;
|
||||
if (rs.next()) {
|
||||
reqLevel = rs.getInt("req_level");
|
||||
reqMakerLevel = rs.getInt("req_maker_level");
|
||||
|
||||
@@ -35,7 +35,9 @@ import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import client.MapleCharacter;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import tools.DatabaseConnection;
|
||||
@@ -57,45 +59,106 @@ public class MapleSkillbookInformationProvider {
|
||||
public enum SkillBookEntry {
|
||||
UNAVAILABLE,
|
||||
QUEST,
|
||||
QUEST_BOOK,
|
||||
QUEST_REWARD,
|
||||
REACTOR,
|
||||
SCRIPT
|
||||
}
|
||||
|
||||
static String host = "jdbc:mysql://localhost:3306/heavenms";
|
||||
static String driver = "com.mysql.jdbc.Driver";
|
||||
static String username = "root";
|
||||
static String password = "";
|
||||
private static String host = "jdbc:mysql://localhost:3306/heavenms";
|
||||
private static String driver = "com.mysql.jdbc.Driver";
|
||||
private static String username = "root";
|
||||
private static String password = "";
|
||||
|
||||
static String rootDirectory = ".";
|
||||
private static String rootDirectory = ".";
|
||||
|
||||
static int skillbookMinItemid = 2280000;
|
||||
static int skillbookMaxItemid = 2300000; // exclusively
|
||||
private static int skillbookMinItemid = 2280000;
|
||||
private static int skillbookMaxItemid = 2300000; // exclusively
|
||||
|
||||
private static Set<Integer> questSkills = new HashSet<>();
|
||||
|
||||
static {
|
||||
loadSkillbooks();
|
||||
}
|
||||
|
||||
public static boolean isSkillBook(int itemid) {
|
||||
private static boolean is4thJobSkill(int itemid) {
|
||||
return itemid / 10000 % 10 == 2;
|
||||
}
|
||||
|
||||
private static boolean isSkillBook(int itemid) {
|
||||
return itemid >= skillbookMinItemid && itemid < skillbookMaxItemid;
|
||||
}
|
||||
|
||||
private static boolean isQuestBook(int itemid) {
|
||||
return itemid >= 4001107 && itemid <= 4001114 || itemid >= 4161015 && itemid <= 4161023;
|
||||
}
|
||||
|
||||
private static int fetchQuestbook(MapleData checkData, String quest) {
|
||||
MapleData questStartData = checkData.getChildByPath(quest).getChildByPath("0");
|
||||
|
||||
MapleData startReqItemData = questStartData.getChildByPath("item");
|
||||
if (startReqItemData != null) {
|
||||
for (MapleData itemData : startReqItemData.getChildren()) {
|
||||
int itemid = MapleDataTool.getInt("id", itemData, 0);
|
||||
if (isQuestBook(itemid)) {
|
||||
return itemid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapleData startReqQuestData = questStartData.getChildByPath("quest");
|
||||
if (startReqQuestData != null) {
|
||||
Set<Integer> reqQuests = new HashSet<>();
|
||||
|
||||
for (MapleData questStatusData : startReqQuestData.getChildren()) {
|
||||
int reqQuest = MapleDataTool.getInt("id", questStatusData, 0);
|
||||
if (reqQuest > 0) {
|
||||
reqQuests.add(reqQuest);
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer reqQuest : reqQuests) {
|
||||
int book = fetchQuestbook(checkData, Integer.toString(reqQuest));
|
||||
if (book > -1) {
|
||||
return book;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void fetchSkillbooksFromQuests() {
|
||||
MapleData actData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz")).getData("Act.img");
|
||||
MapleDataProvider questDataProvider = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/" + "Quest.wz"));
|
||||
MapleData actData = questDataProvider.getData("Act.img");
|
||||
MapleData checkData = questDataProvider.getData("Check.img");
|
||||
|
||||
for (MapleData questData : actData.getChildren()) {
|
||||
for (MapleData questStatusData : questData.getChildren()) {
|
||||
for (MapleData questNodeData : questStatusData.getChildren()) {
|
||||
if (questNodeData.getName().contentEquals("item")) {
|
||||
String actNodeName = questNodeData.getName();
|
||||
if (actNodeName.contentEquals("item")) {
|
||||
for (MapleData questItemData : questNodeData.getChildren()) {
|
||||
int itemid = MapleDataTool.getInt("id", questItemData, 0);
|
||||
int itemcount = MapleDataTool.getInt("count", questItemData, 0);
|
||||
|
||||
if (isSkillBook(itemid) && itemcount > 0) {
|
||||
int questbook = fetchQuestbook(checkData, questData.getName());
|
||||
if (questbook < 0) {
|
||||
foundSkillbooks.put(itemid, SkillBookEntry.QUEST);
|
||||
} else {
|
||||
foundSkillbooks.put(itemid, SkillBookEntry.QUEST_BOOK);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (actNodeName.contentEquals("skill")) {
|
||||
for (MapleData questSkillData : questNodeData.getChildren()) {
|
||||
int skillid = MapleDataTool.getInt("id", questSkillData, 0);
|
||||
if (is4thJobSkill(skillid)) {
|
||||
// negative itemids are skill rewards
|
||||
foundSkillbooks.put(-skillid, SkillBookEntry.QUEST_REWARD);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,4 +275,23 @@ public class MapleSkillbookInformationProvider {
|
||||
return sbe != null ? sbe : SkillBookEntry.UNAVAILABLE;
|
||||
}
|
||||
|
||||
public List<Integer> getTeachableSkills(MapleCharacter chr) {
|
||||
List<Integer> list = new ArrayList<>();
|
||||
|
||||
for (Integer book : foundSkillbooks.keySet()) {
|
||||
if (book >= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int skillid = -book;
|
||||
if (skillid / 10000 == chr.getJob().getId()) {
|
||||
if (chr.getMasterLevel(skillid) == 0) {
|
||||
list.add(-skillid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ public class MapleStatEffect {
|
||||
private int itemCon, itemConNo;
|
||||
private int damage, attackCount, fixdamage;
|
||||
private Point lt, rb;
|
||||
private byte bulletCount, bulletConsume;
|
||||
private short bulletCount, bulletConsume;
|
||||
private byte mapProtection;
|
||||
private CardItemupStats cardStats;
|
||||
|
||||
@@ -484,8 +484,8 @@ public class MapleStatEffect {
|
||||
ret.damage = MapleDataTool.getIntConvert("damage", source, 100);
|
||||
ret.fixdamage = MapleDataTool.getIntConvert("fixdamage", source, -1);
|
||||
ret.attackCount = MapleDataTool.getIntConvert("attackCount", source, 1);
|
||||
ret.bulletCount = (byte) MapleDataTool.getIntConvert("bulletCount", source, 1);
|
||||
ret.bulletConsume = (byte) MapleDataTool.getIntConvert("bulletConsume", source, 0);
|
||||
ret.bulletCount = (short) MapleDataTool.getIntConvert("bulletCount", source, 1);
|
||||
ret.bulletConsume = (short) MapleDataTool.getIntConvert("bulletConsume", source, 0);
|
||||
ret.moneyCon = MapleDataTool.getIntConvert("moneyCon", source, 0);
|
||||
ret.itemCon = MapleDataTool.getInt("itemCon", source, 0);
|
||||
ret.itemConNo = MapleDataTool.getInt("itemConNo", source, 0);
|
||||
@@ -961,6 +961,7 @@ public class MapleStatEffect {
|
||||
} else if (isCureAllAbnormalStatus()) {
|
||||
applyto.dispelDebuff(MapleDisease.SEDUCE);
|
||||
applyto.dispelDebuff(MapleDisease.ZOMBIFY);
|
||||
applyto.dispelDebuff(MapleDisease.CONFUSE);
|
||||
applyto.dispelDebuffs();
|
||||
} else if (isComboReset()) {
|
||||
applyto.setCombo((short) 0);
|
||||
@@ -1008,6 +1009,8 @@ public class MapleStatEffect {
|
||||
}
|
||||
}
|
||||
if (isShadowClaw()) {
|
||||
short projectileConsume = this.getBulletConsume(); // noticed by shavit
|
||||
|
||||
MapleInventory use = applyto.getInventory(MapleInventoryType.USE);
|
||||
use.lockInventory();
|
||||
try {
|
||||
@@ -1015,7 +1018,7 @@ public class MapleStatEffect {
|
||||
for (int i = 1; i <= use.getSlotLimit(); i++) { // impose order...
|
||||
Item item = use.getItem((short) i);
|
||||
if (item != null) {
|
||||
if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= 200) {
|
||||
if (ItemConstants.isThrowingStar(item.getItemId()) && item.getQuantity() >= projectileConsume) {
|
||||
projectile = item;
|
||||
break;
|
||||
}
|
||||
@@ -1024,7 +1027,7 @@ public class MapleStatEffect {
|
||||
if (projectile == null) {
|
||||
return false;
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) 200, false, true);
|
||||
MapleInventoryManipulator.removeFromSlot(applyto.getClient(), MapleInventoryType.USE, projectile.getPosition(), (short) projectileConsume, false, true);
|
||||
}
|
||||
} finally {
|
||||
use.unlockInventory();
|
||||
@@ -1938,11 +1941,11 @@ public class MapleStatEffect {
|
||||
return fixdamage;
|
||||
}
|
||||
|
||||
public byte getBulletCount() {
|
||||
public short getBulletCount() {
|
||||
return bulletCount;
|
||||
}
|
||||
|
||||
public byte getBulletConsume() {
|
||||
public short getBulletConsume() {
|
||||
return bulletConsume;
|
||||
}
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ public class MobSkill {
|
||||
break;
|
||||
case 127:
|
||||
if (lt != null && rb != null && skill) {
|
||||
for (MapleCharacter character : getPlayersInRange(monster, player)) {
|
||||
for (MapleCharacter character : getPlayersInRange(monster)) {
|
||||
character.dispel();
|
||||
}
|
||||
} else {
|
||||
@@ -192,7 +192,7 @@ public class MobSkill {
|
||||
break;
|
||||
case 129: // Banish
|
||||
if (lt != null && rb != null && skill) {
|
||||
for (MapleCharacter chr : getPlayersInRange(monster, player)) {
|
||||
for (MapleCharacter chr : getPlayersInRange(monster)) {
|
||||
banishPlayers.add(chr);
|
||||
}
|
||||
} else {
|
||||
@@ -200,7 +200,7 @@ public class MobSkill {
|
||||
}
|
||||
break;
|
||||
case 131: // Mist
|
||||
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), monster, this), x * 100, false, false, false);
|
||||
monster.getMap().spawnMist(new MapleMist(calculateBoundingBox(monster.getPosition()), monster, this), x * 100, false, false, false);
|
||||
break;
|
||||
case 132:
|
||||
disease = MapleDisease.CONFUSE;
|
||||
@@ -248,7 +248,7 @@ public class MobSkill {
|
||||
int skillLimit = this.getLimit();
|
||||
MapleMap map = monster.getMap();
|
||||
|
||||
if (map.isDojoMap()) { // spawns in dojo should be unlimited
|
||||
if (GameConstants.isDojo(map.getId())) { // spawns in dojo should be unlimited
|
||||
skillLimit = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ public class MobSkill {
|
||||
if (disease != null) {
|
||||
if (lt != null && rb != null && skill) {
|
||||
int i = 0;
|
||||
for (MapleCharacter character : getPlayersInRange(monster, player)) {
|
||||
for (MapleCharacter character : getPlayersInRange(monster)) {
|
||||
if (!character.hasActiveBuff(2321005)) { // holy shield
|
||||
if (disease.equals(MapleDisease.SEDUCE)) {
|
||||
if (i < 10) {
|
||||
@@ -351,8 +351,8 @@ public class MobSkill {
|
||||
}
|
||||
}
|
||||
|
||||
private List<MapleCharacter> getPlayersInRange(MapleMonster monster, MapleCharacter player) {
|
||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(player));
|
||||
private List<MapleCharacter> getPlayersInRange(MapleMonster monster) {
|
||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition()));
|
||||
}
|
||||
|
||||
public int getSkillId() {
|
||||
@@ -411,15 +411,14 @@ public class MobSkill {
|
||||
return prop == 1.0 || Math.random() < prop;
|
||||
}
|
||||
|
||||
private Rectangle calculateBoundingBox(Point posFrom, boolean facingLeft) {
|
||||
int multiplier = facingLeft ? 1 : -1;
|
||||
Point mylt = new Point(lt.x * multiplier + posFrom.x, lt.y + posFrom.y);
|
||||
Point myrb = new Point(rb.x * multiplier + posFrom.x, rb.y + posFrom.y);
|
||||
private Rectangle calculateBoundingBox(Point posFrom) {
|
||||
Point mylt = new Point(lt.x + posFrom.x, lt.y + posFrom.y);
|
||||
Point myrb = new Point(rb.x + posFrom.x, rb.y + posFrom.y);
|
||||
Rectangle bounds = new Rectangle(mylt.x, mylt.y, myrb.x - mylt.x, myrb.y - mylt.y);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
private List<MapleMapObject> getObjectsInRange(MapleMonster monster, MapleMapObjectType objectType) {
|
||||
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(objectType));
|
||||
return monster.getMap().getMapObjectsInBox(calculateBoundingBox(monster.getPosition()), Collections.singletonList(objectType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,9 +758,11 @@ public class MapleMap {
|
||||
final List<MonsterDropEntry> dropEntry = new ArrayList<>();
|
||||
final List<MonsterDropEntry> visibleQuestEntry = new ArrayList<>();
|
||||
final List<MonsterDropEntry> otherQuestEntry = new ArrayList<>();
|
||||
sortDropEntries(YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr);
|
||||
|
||||
if (dropEntry.isEmpty() && visibleQuestEntry.isEmpty()) { // thanks resinate
|
||||
List<MonsterDropEntry> lootEntry = YamlConfig.config.server.USE_SPAWN_RELEVANT_LOOT ? mob.retrieveRelevantDrops() : mi.retrieveEffectiveDrop(mob.getId());
|
||||
sortDropEntries(lootEntry, dropEntry, visibleQuestEntry, otherQuestEntry, chr); // thanks Articuno, Limit, Rohenn for noticing quest loots not showing up in only-quest item drops scenario
|
||||
|
||||
if (lootEntry.isEmpty()) { // thanks resinate
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1309,17 +1311,15 @@ public class MapleMap {
|
||||
return pchars;
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> targets) {
|
||||
public List<MapleCharacter> getPlayersInRange(Rectangle box) {
|
||||
List<MapleCharacter> character = new LinkedList<>();
|
||||
chrRLock.lock();
|
||||
try {
|
||||
for (MapleCharacter chr : targets) {
|
||||
if (characters.contains(chr)) {
|
||||
for (MapleCharacter chr : characters) {
|
||||
if (box.contains(chr.getPosition())) {
|
||||
character.add(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
chrRLock.unlock();
|
||||
}
|
||||
@@ -4173,14 +4173,6 @@ public class MapleMap {
|
||||
this.setDocked(state);
|
||||
}
|
||||
|
||||
public boolean isDojoMap() {
|
||||
return mapid >= 925020000 && mapid < 925040000;
|
||||
}
|
||||
|
||||
public boolean isDojoFightMap() {
|
||||
return isDojoMap() && (((mapid / 100) % 100) % 6) > 0;
|
||||
}
|
||||
|
||||
public boolean isHorntailDefeated() { // all parts of dead horntail can be found here?
|
||||
for(int i = 8810010; i <= 8810017; i++) {
|
||||
if (getMonsterById(i) == null) {
|
||||
|
||||
@@ -272,13 +272,13 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
|
||||
int price = (int) Math.min((float)pItem.getPrice() * quantity, Integer.MAX_VALUE);
|
||||
|
||||
if (c.getPlayer().getMeso() >= price) {
|
||||
if (canBuy(c, newItem)) {
|
||||
if (!owner.canHoldMeso(price)) {
|
||||
owner.dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos.");
|
||||
if (!owner.canHoldMeso(price)) { // thanks Rohenn for noticing owner hold check misplaced
|
||||
c.getPlayer().dropMessage(1, "Transaction failed since the shop owner can't hold any more mesos.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (canBuy(c, newItem)) {
|
||||
c.getPlayer().gainMeso(-price, false);
|
||||
price -= MapleTrade.getFee(price); // thanks BHB for pointing out trade fees not applying here
|
||||
owner.gainMeso(price, true);
|
||||
|
||||
@@ -61,6 +61,7 @@ public class ItemAction extends MapleQuestAction {
|
||||
for (MapleData iEntry : data.getChildren()) {
|
||||
int id = MapleDataTool.getInt(iEntry.getChildByPath("id"));
|
||||
int count = MapleDataTool.getInt(iEntry.getChildByPath("count"), 1);
|
||||
int period = MapleDataTool.getInt(iEntry.getChildByPath("period"), 0);
|
||||
|
||||
Integer prop = null;
|
||||
MapleData propData = iEntry.getChildByPath("prop");
|
||||
@@ -75,7 +76,7 @@ public class ItemAction extends MapleQuestAction {
|
||||
if (iEntry.getChildByPath("job") != null)
|
||||
job = MapleDataTool.getInt(iEntry.getChildByPath("job"));
|
||||
|
||||
items.add(new ItemData(Integer.parseInt(iEntry.getName()), id, count, prop, job, gender));
|
||||
items.add(new ItemData(Integer.parseInt(iEntry.getName()), id, count, prop, job, gender, period));
|
||||
}
|
||||
|
||||
Collections.sort(items, new Comparator<ItemData>()
|
||||
@@ -90,8 +91,8 @@ public class ItemAction extends MapleQuestAction {
|
||||
|
||||
@Override
|
||||
public void run(MapleCharacter chr, Integer extSelection) {
|
||||
List<Pair<Integer, Integer>> takeItem = new LinkedList<>();
|
||||
List<Pair<Integer, Integer>> giveItem = new LinkedList<>();
|
||||
List<ItemData> takeItem = new LinkedList<>();
|
||||
List<ItemData> giveItem = new LinkedList<>();
|
||||
|
||||
int props = 0, rndProps = 0, accProps = 0;
|
||||
for(ItemData item : items) {
|
||||
@@ -125,34 +126,38 @@ public class ItemAction extends MapleQuestAction {
|
||||
}
|
||||
|
||||
if(iEntry.getCount() < 0) { // Remove Item
|
||||
takeItem.add(new Pair<>(iEntry.getId(), iEntry.getCount()));
|
||||
takeItem.add(iEntry);
|
||||
} else { // Give Item
|
||||
giveItem.add(new Pair<>(iEntry.getId(), iEntry.getCount()));
|
||||
giveItem.add(iEntry);
|
||||
}
|
||||
}
|
||||
|
||||
// must take all needed items before giving others
|
||||
|
||||
for(Pair<Integer, Integer> iPair: takeItem) {
|
||||
MapleInventoryType type = ItemConstants.getInventoryType(iPair.getLeft());
|
||||
int quantity = iPair.getRight() * -1; // Invert
|
||||
for(ItemData iEntry: takeItem) {
|
||||
int itemid = iEntry.getId(), count = iEntry.getCount();
|
||||
|
||||
MapleInventoryType type = ItemConstants.getInventoryType(itemid);
|
||||
int quantity = count * -1; // Invert
|
||||
if(type.equals(MapleInventoryType.EQUIP)) {
|
||||
if(chr.getInventory(type).countById(iPair.getLeft()) < quantity) {
|
||||
if(chr.getInventory(type).countById(itemid) < quantity) {
|
||||
// Not enough in the equip inventoty, so check Equipped...
|
||||
if(chr.getInventory(MapleInventoryType.EQUIPPED).countById(iPair.getLeft()) > quantity) {
|
||||
if(chr.getInventory(MapleInventoryType.EQUIPPED).countById(itemid) > quantity) {
|
||||
// Found it equipped, so change the type to equipped.
|
||||
type = MapleInventoryType.EQUIPPED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeById(chr.getClient(), type, iPair.getLeft(), quantity, true, false);
|
||||
chr.announce(MaplePacketCreator.getShowItemGain(iPair.getLeft(), (short) iPair.getRight().shortValue(), true));
|
||||
MapleInventoryManipulator.removeById(chr.getClient(), type, itemid, quantity, true, false);
|
||||
chr.announce(MaplePacketCreator.getShowItemGain(itemid, (short) count, true));
|
||||
}
|
||||
|
||||
for(Pair<Integer, Integer> iPair: giveItem) {
|
||||
MapleInventoryManipulator.addById(chr.getClient(), iPair.getLeft(), (short) iPair.getRight().shortValue(), "", -1);
|
||||
chr.announce(MaplePacketCreator.getShowItemGain(iPair.getLeft(), (short) iPair.getRight().shortValue(), true));
|
||||
for(ItemData iEntry: giveItem) {
|
||||
int itemid = iEntry.getId(), count = iEntry.getCount(), period = iEntry.getPeriod(); // thanks Vcoc for noticing quest milestone item not getting removed from inventory after a while
|
||||
|
||||
MapleInventoryManipulator.addById(chr.getClient(), itemid, (short) count, "", -1, period > 0 ? (System.currentTimeMillis() + period * 60 * 1000) : -1);
|
||||
chr.announce(MaplePacketCreator.getShowItemGain(itemid, (short) count, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,16 +325,17 @@ public class ItemAction extends MapleQuestAction {
|
||||
}
|
||||
|
||||
private class ItemData {
|
||||
private final int map, id, count, job, gender;
|
||||
private final int map, id, count, job, gender, period;
|
||||
private final Integer prop;
|
||||
|
||||
public ItemData(int map, int id, int count, Integer prop, int job, int gender) {
|
||||
public ItemData(int map, int id, int count, Integer prop, int job, int gender, int period) {
|
||||
this.map = map;
|
||||
this.id = id;
|
||||
this.count = count;
|
||||
this.prop = prop;
|
||||
this.job = job;
|
||||
this.gender = gender;
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
@@ -351,5 +357,9 @@ public class ItemAction extends MapleQuestAction {
|
||||
public int getGender() {
|
||||
return gender;
|
||||
}
|
||||
|
||||
public int getPeriod() {
|
||||
return period;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4695,10 +4695,10 @@
|
||||
</imgdir>
|
||||
<imgdir name="spec">
|
||||
<int name="time" value="300000"/>
|
||||
<int name="mhpRRate" value="5"/>
|
||||
<int name="mhpRRate" value="10"/>
|
||||
<int name="mhpR" value="-100"/>
|
||||
<int name="mmpR" value="-100"/>
|
||||
<int name="mmpRRate" value="5"/>
|
||||
<int name="mmpRRate" value="10"/>
|
||||
</imgdir>
|
||||
</imgdir>
|
||||
<imgdir name="02022338">
|
||||
|
||||
Reference in New Issue
Block a user