Item Raise + Gachapon Rework + Minigames & M. Magnet & M. Door patch
Fixed a glitch in inventory that would happen when trying to put items into storage. Added "Item Raise" functionality. Adjusted Party Search. Token sessions no longer expires due to no players found, rather are sent into a brief "inactivity" period. Fixed Fredrick stored items not being properly erased after a character deletion. Fixed skillbooks in stacked quantities not being useable. New tool: MapleGachaponItemidRetriever. This tool parses the gachapon descriptor file (holding several item names, for several gachapons) and generates files for each defined gachapon with itemids of the loots. Revised Mushroom Empire transition portals that interacts with scripted items (items now are useable through inventory, no longer being automatically removed from inventory upon crossing portals). Fixed script "secretroom" always requiring a new key (quest reward) to access the inner rooms, which would make the room unreachable. Reworked gachapons loots throughout the game. New loots are supposed to represent an old-school MapleSEA-like escalation of gachapon loots. Fixed issues that would show up in the case null PIN/PIC gets checked. Reworked skill Monster Magnet, no longer using "catch success rate" as a skill progression element. This fixes the skill disconnecting caster upon failure. Revised Mystic Doors, no longer crashing players after the caster decides to cancel the buff moments after casting (during portal deploying effect in course). Fixed portal access to Prime Minister crashing when trying to access on a party. Refactored the several "startQuest/completeQuest" methods widely used in the many scripting managers. Methods that does essentially the same thing now are accessed from the superclass. Fixed "rechargeable items" not being properly accounted for slots availability in inventory slot checking. Fixed several issues of late within minigames. Fixed minigames regarding double results when handling some rare scenarios. Implemented "call for leave after finishing game" functionality of minigames.
This commit is contained in:
@@ -495,15 +495,12 @@ public class Server {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetServerWorlds() {
|
||||
private void resetServerWorlds() { // thanks maple006 for noticing proprietary lists assigned to null
|
||||
wldWLock.lock();
|
||||
try {
|
||||
worlds.clear();
|
||||
worlds = null;
|
||||
channels.clear();
|
||||
channels = null;
|
||||
worldRecommendedList.clear();
|
||||
worldRecommendedList = null;
|
||||
} finally {
|
||||
wldWLock.unlock();
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
ANSWER_TIE(0x33),
|
||||
GIVE_UP(0x34),
|
||||
EXIT_AFTER_GAME(0x38),
|
||||
CANCEL_EXIT(0x39),
|
||||
CANCEL_EXIT_AFTER_GAME(0x39),
|
||||
READY(0x3A),
|
||||
UN_READY(0x3B),
|
||||
EXPEL(0x3C),
|
||||
@@ -344,7 +344,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MapleTrade.cancelTrade(chr, MapleTrade.TradeResult.PARTNER_CANCEL);
|
||||
} else {
|
||||
chr.closePlayerShop();
|
||||
chr.closeMiniGame();
|
||||
chr.closeMiniGame(false);
|
||||
chr.closeHiredMerchant(true);
|
||||
}
|
||||
} else if (mode == Action.OPEN_STORE.getCode() || mode == Action.OPEN_CASH.getCode()) {
|
||||
@@ -608,8 +608,6 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MaplePlayerShop shop = chr.getPlayerShop();
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
if (shop != null && shop.isOwner(chr)) {
|
||||
System.out.println(shopItem.getItem().getPet() + " " + shopItem.getItem().getPetId());
|
||||
System.out.println(ivItem.getPet() + " " + ivItem.getPetId());
|
||||
if (shop.isOpen() || !shop.addItem(shopItem)) { // thanks Vcoc for pointing an exploit with unlimited shop slots
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
|
||||
return;
|
||||
@@ -770,10 +768,20 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
|
||||
MapleCharacter visitor = miniGame.getVisitor();
|
||||
|
||||
if(visitor != null) {
|
||||
visitor.closeMiniGame();
|
||||
visitor.announce(MaplePacketCreator.getMiniGameClose(5));
|
||||
visitor.closeMiniGame(false);
|
||||
visitor.announce(MaplePacketCreator.getMiniGameClose(true, 5));
|
||||
}
|
||||
}
|
||||
} else if (mode == Action.EXIT_AFTER_GAME.getCode()) {
|
||||
MapleMiniGame miniGame = chr.getMiniGame();
|
||||
if(miniGame != null) {
|
||||
miniGame.setQuitAfterGame(chr, true);
|
||||
}
|
||||
} else if (mode == Action.CANCEL_EXIT_AFTER_GAME.getCode()) {
|
||||
MapleMiniGame miniGame = chr.getMiniGame();
|
||||
if(miniGame != null) {
|
||||
miniGame.setQuitAfterGame(chr, false);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
|
||||
59
src/net/server/channel/handlers/RaiseIncExpHandler.java
Normal file
59
src/net/server/channel/handlers/RaiseIncExpHandler.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleItemInformationProvider.QuestConsItem;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Xari
|
||||
* @author Ronan - added concurrency protection and quest progress limit
|
||||
*/
|
||||
public class RaiseIncExpHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
byte inventorytype = slea.readByte();//nItemIT
|
||||
short slot = slea.readShort();//nSlotPosition
|
||||
int itemid = slea.readInt();//nItemID
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
QuestConsItem consItem = ii.getQuestConsumablesInfo(itemid);
|
||||
if (consItem == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int questid = consItem.questid;
|
||||
Map<Integer, Integer> consumables = consItem.items;
|
||||
|
||||
int consId;
|
||||
MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.getByType(inventorytype));
|
||||
inv.lockInventory();
|
||||
try {
|
||||
consId = inv.getItem(slot).getItemId();
|
||||
if (!consumables.containsKey(consId) || !c.getPlayer().haveItem(consId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.getByType(inventorytype), (short) slot, (short) 1, false, true);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
int nextValue = Math.min(consumables.get(consId) + Integer.parseInt(c.getPlayer().getQuestInfo(questid)), consItem.exp * consItem.grade);
|
||||
c.getPlayer().updateQuestInfo(questid, "" + nextValue);
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/net/server/channel/handlers/RaiseUIStateHandler.java
Normal file
37
src/net/server/channel/handlers/RaiseUIStateHandler.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleQuestStatus;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.quest.MapleQuest;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Xari
|
||||
*/
|
||||
public class RaiseUIStateHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int questid = slea.readShort();
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleQuest quest = MapleQuest.getInstance(questid);
|
||||
MapleQuestStatus mqs = c.getPlayer().getQuest(quest);
|
||||
if (mqs.getStatus() == MapleQuestStatus.Status.NOT_STARTED) {
|
||||
quest.forceStart(c.getPlayer(), 22000);
|
||||
c.getPlayer().updateQuestInfo(quest.getId(), "0");
|
||||
} else if (mqs.getStatus() == MapleQuestStatus.Status.STARTED) {
|
||||
c.announce(MaplePacketCreator.updateQuest(mqs, false));
|
||||
} else {
|
||||
//c.announce(MaplePacketCreator.updateQuestInfo(mqs.getQuestID(), 22000, "0"));
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import java.util.Map;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
@@ -41,38 +42,57 @@ public final class SkillBookHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
slea.readInt();
|
||||
short slot = (short) slea.readShort();
|
||||
int itemId = slea.readInt();
|
||||
|
||||
boolean canuse;
|
||||
boolean success = false;
|
||||
int skill = 0;
|
||||
int maxlevel = 0;
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
Item toUse = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(slot);
|
||||
if (toUse != null && toUse.getQuantity() == 1) {
|
||||
if (toUse.getItemId() != itemId) {
|
||||
return;
|
||||
}
|
||||
Map<String, Integer> skilldata = MapleItemInformationProvider.getInstance().getSkillStats(toUse.getItemId(), c.getPlayer().getJob().getId());
|
||||
boolean canuse;
|
||||
boolean success = false;
|
||||
int skill = 0;
|
||||
int maxlevel = 0;
|
||||
if (skilldata == null) {
|
||||
return;
|
||||
}
|
||||
Skill skill2 = SkillFactory.getSkill(skilldata.get("skillid"));
|
||||
if (skilldata.get("skillid") == 0) {
|
||||
canuse = false;
|
||||
} else if ((player.getSkillLevel(skill2) >= skilldata.get("reqSkillLevel") || skilldata.get("reqSkillLevel") == 0) && player.getMasterLevel(skill2) < skilldata.get("masterLevel")) {
|
||||
canuse = true;
|
||||
if (MapleItemInformationProvider.rollSuccessChance(skilldata.get("success"))) {
|
||||
success = true;
|
||||
player.changeSkillLevel(skill2, player.getSkillLevel(skill2), Math.max(skilldata.get("masterLevel"), player.getMasterLevel(skill2)), -1);
|
||||
} else {
|
||||
success = false;
|
||||
//player.dropMessage("The skill book lights up, but the skill winds up as if nothing happened.");
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleInventory inv = c.getPlayer().getInventory(MapleInventoryType.USE);
|
||||
Item toUse = inv.getItem(slot);
|
||||
if (toUse == null || toUse.getItemId() != itemId) {
|
||||
return;
|
||||
}
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
|
||||
} else {
|
||||
canuse = false;
|
||||
Map<String, Integer> skilldata = MapleItemInformationProvider.getInstance().getSkillStats(toUse.getItemId(), c.getPlayer().getJob().getId());
|
||||
if (skilldata == null) {
|
||||
return;
|
||||
}
|
||||
Skill skill2 = SkillFactory.getSkill(skilldata.get("skillid"));
|
||||
if (skilldata.get("skillid") == 0) {
|
||||
canuse = false;
|
||||
} else if ((player.getSkillLevel(skill2) >= skilldata.get("reqSkillLevel") || skilldata.get("reqSkillLevel") == 0) && player.getMasterLevel(skill2) < skilldata.get("masterLevel")) {
|
||||
inv.lockInventory();
|
||||
try {
|
||||
Item used = inv.getItem(slot);
|
||||
if (used != toUse || toUse.getQuantity() < 1) { // thanks ClouD for noticing skillbooks not being usable when stacked
|
||||
return;
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, (short) 1, false);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
canuse = true;
|
||||
if (MapleItemInformationProvider.rollSuccessChance(skilldata.get("success"))) {
|
||||
success = true;
|
||||
player.changeSkillLevel(skill2, player.getSkillLevel(skill2), Math.max(skilldata.get("masterLevel"), player.getMasterLevel(skill2)), -1);
|
||||
} else {
|
||||
success = false;
|
||||
//player.dropMessage("The skill book lights up, but the skill winds up as if nothing happened.");
|
||||
}
|
||||
} else {
|
||||
canuse = false;
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
|
||||
// thanks Vcoc for noting skill book result not showing for all in area
|
||||
|
||||
@@ -101,6 +101,7 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
monster.aggroClearDamages();
|
||||
monster.aggroMonsterDamage(chr, 1);
|
||||
|
||||
// thanks onechord for pointing out Magnet disconnecting the caster (issue would actually happen upon failing to catch mob)
|
||||
monster.aggroSwitchController(chr, true);
|
||||
}
|
||||
}
|
||||
@@ -133,12 +134,20 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
|
||||
} else {
|
||||
skill.getEffect(skillLevel).applyEchoOfHero(chr);
|
||||
}
|
||||
} else if(chr.canDoor()) {
|
||||
//update door lists
|
||||
chr.cancelMagicDoor();
|
||||
skill.getEffect(skillLevel).applyTo(chr, pos);
|
||||
} else {
|
||||
chr.message("Please wait 5 seconds before casting Mystic Door again.");
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if (chr.canDoor()) {
|
||||
chr.cancelMagicDoor();
|
||||
skill.getEffect(skillLevel).applyTo(chr, pos);
|
||||
} else {
|
||||
chr.message("Please wait 5 seconds before casting Mystic Door again.");
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -130,7 +130,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
if (victim != null) {
|
||||
MapleMap targetMap = victim.getMap();
|
||||
if (!FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit()) && (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000)) {
|
||||
if (victim.gmLevel() <= player.gmLevel()) {
|
||||
if (!victim.isGM() || victim.gmLevel() <= player.gmLevel()) { // thanks Yoboes for noticing non-GM's being unreachable through rocks
|
||||
player.forceChangeMap(targetMap, targetMap.findClosestPlayerSpawnpoint(victim.getPosition()));
|
||||
success = true;
|
||||
} else {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
public final class UseItemCanvasHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
@@ -64,6 +64,10 @@ public class MaplePartySearchCoordinator {
|
||||
private Map<Integer, MapleCharacter> searchLeaders = new HashMap<>();
|
||||
private Map<Integer, LeaderSearchMetadata> searchSettings = new HashMap<>();
|
||||
|
||||
private Map<MapleCharacter, LeaderSearchMetadata> timeoutLeaders = new HashMap<>();
|
||||
|
||||
private int updateCount = 0;
|
||||
|
||||
private static Map<Integer, Set<Integer>> mapNeighbors = fetchNeighbouringMaps();
|
||||
private static Map<Integer, MapleJob> jobTable = instantiateJobTable();
|
||||
|
||||
@@ -245,11 +249,21 @@ public class MaplePartySearchCoordinator {
|
||||
addQueueLeader(leader);
|
||||
}
|
||||
|
||||
private void registerPartyLeader(MapleCharacter leader, LeaderSearchMetadata settings) {
|
||||
if (searchLeaders.containsKey(leader.getId())) return;
|
||||
|
||||
searchSettings.put(leader.getId(), settings);
|
||||
searchLeaders.put(leader.getId(), leader);
|
||||
addQueueLeader(leader);
|
||||
}
|
||||
|
||||
public void unregisterPartyLeader(MapleCharacter leader) {
|
||||
MapleCharacter toRemove = searchLeaders.remove(leader.getId());
|
||||
if (toRemove != null) {
|
||||
removeQueueLeader(toRemove);
|
||||
searchSettings.remove(leader.getId());
|
||||
} else {
|
||||
unregisterLongTermPartyLeader(leader);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +321,41 @@ public class MaplePartySearchCoordinator {
|
||||
return new Pair<>(queuedLeaders, nextLeaders);
|
||||
}
|
||||
|
||||
private void registerLongTermPartyLeaders(List<Pair<MapleCharacter, LeaderSearchMetadata>> recycledLeaders) {
|
||||
leaderQueueRLock.lock();
|
||||
try {
|
||||
for (Pair<MapleCharacter, LeaderSearchMetadata> p : recycledLeaders) {
|
||||
timeoutLeaders.put(p.getLeft(), p.getRight());
|
||||
}
|
||||
} finally {
|
||||
leaderQueueRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterLongTermPartyLeader(MapleCharacter leader) {
|
||||
leaderQueueRLock.lock();
|
||||
try {
|
||||
timeoutLeaders.remove(leader);
|
||||
} finally {
|
||||
leaderQueueRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void reinstateLongTermPartyLeaders() {
|
||||
Map<MapleCharacter, LeaderSearchMetadata> timeoutLeadersCopy;
|
||||
leaderQueueWLock.lock();
|
||||
try {
|
||||
timeoutLeadersCopy = new HashMap<>(timeoutLeaders);
|
||||
timeoutLeaders.clear();
|
||||
} finally {
|
||||
leaderQueueWLock.unlock();
|
||||
}
|
||||
|
||||
for (Entry<MapleCharacter, LeaderSearchMetadata> e : timeoutLeadersCopy.entrySet()) {
|
||||
registerPartyLeader(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public void runPartySearch() {
|
||||
Pair<List<MapleCharacter>, List<MapleCharacter>> queuedLeaders = fetchQueuedLeaders();
|
||||
|
||||
@@ -356,10 +405,28 @@ public class MaplePartySearchCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
List<Pair<MapleCharacter, LeaderSearchMetadata>> recycledLeaders = new LinkedList<>();
|
||||
for (MapleCharacter leader : expiredLeaders) {
|
||||
if (leader.isLoggedinWorld()) leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later.");
|
||||
searchLeaders.remove(leader.getId());
|
||||
searchSettings.remove(leader.getId());
|
||||
LeaderSearchMetadata settings = searchSettings.remove(leader.getId());
|
||||
|
||||
if (leader.isLoggedinWorld()) {
|
||||
if (settings != null) {
|
||||
recycledLeaders.add(new Pair<>(leader, settings));
|
||||
if (ServerConstants.USE_DEBUG && leader.isGM()) leader.dropMessage(5, "Your Party Search token session is now on waiting queue for up to 7 minutes, to get it working right away please stop your Party Search and retry again later.");
|
||||
} else {
|
||||
leader.dropMessage(5, "Your Party Search token session expired, please stop your Party Search and retry again later.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!recycledLeaders.isEmpty()) {
|
||||
registerLongTermPartyLeaders(recycledLeaders);
|
||||
}
|
||||
|
||||
updateCount++;
|
||||
if (updateCount % 77 == 0) {
|
||||
reinstateLongTermPartyLeaders();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public final class AfterLoginHandler extends AbstractMaplePacketHandler {
|
||||
c3 = slea.readByte();
|
||||
}
|
||||
if (c2 == 1 && c3 == 1) {
|
||||
if (c.getPin() == null) {
|
||||
if (c.getPin() == null || c.getPin().equals("")) {
|
||||
c.announce(MaplePacketCreator.registerPin());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.requestPin());
|
||||
|
||||
Reference in New Issue
Block a user