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:
ronancpl
2019-06-01 21:12:40 -03:00
parent 89eca2995b
commit edb3920852
111 changed files with 6597 additions and 1943 deletions

View File

@@ -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();
}

View File

@@ -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();

View 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();
}
}
}
}

View 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();
}
}
}
}

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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());
}
}

View File

@@ -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();
}
}

View File

@@ -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());