/*
OrpheusMS: MapleStory Private Server based on OdinMS
Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net>
Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.handlers.channel;
import client.IItem;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleInventoryType;
import constants.ItemConstants;
import java.util.Arrays;
import net.AbstractMaplePacketHandler;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleMiniGame;
import server.MaplePlayerShop;
import server.MaplePlayerShopItem;
import server.MapleTrade;
import server.maps.FieldLimit;
import server.maps.HiredMerchant;
import server.maps.HiredMerchantMessage;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Matze
*/
public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
public enum Action {
CREATE(0), INVITE(2), DECLINE(3), VISIT(4), ROOM(5), CHAT(6), CHAT_THING(8), EXIT(0xA), OPEN(0xB), TRADE_BIRTHDAY(0x0E), SET_ITEMS(0xF), SET_MESO(0x10), CONFIRM(0x11), TRANSACTION(0x14), ADD_ITEM(0x16), BUY(0x17), UPDATE_MERCHANT(0x19), REMOVE_ITEM(0x1B), BAN_PLAYER(0x1C), MERCHANT_THING(0x1D), OPEN_STORE(0x1E), PUT_ITEM(0x21), MERCHANT_BUY(0x22), TAKE_ITEM_BACK(0x26), MAINTENANCE_OFF(0x27), MERCHANT_ORGANIZE(0x28), CLOSE_MERCHANT(0x29), REAL_CLOSE_MERCHANT(0x2A), MERCHANT_MESO(0x2B), SOMETHING(0x2D), VIEW_VISITORS(0x2E), BLACKLIST(0x2F), REQUEST_TIE(0x32), ANSWER_TIE(0x33), GIVE_UP(0x34), EXIT_AFTER_GAME(0x38), CANCEL_EXIT(0x39), READY(0x3A), UN_READY(0x3B), START(0x3D), GET_RESULT(0x3E), SKIP(0x3F), MOVE_OMOK(0x40), SELECT_CARD(0x44);
final byte code;
private Action(int code) {
this.code = (byte) code;
}
public byte getCode() {
return code;
}
}
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
byte mode = slea.readByte();
MapleCharacter chr = c.getPlayer();
if (mode == Action.CREATE.getCode()) {
byte createType = slea.readByte();
if (createType == 3) {// trade
MapleTrade.startTrade(chr);
} else if (createType == 1) { // omok mini game
if (chr.getChalkboard() != null || FieldLimit.CANNOTMINIGAME.check(chr.getMap().getFieldLimit())) {
return;
}
String desc = slea.readMapleAsciiString();
slea.readByte(); // 20 6E 4E
int type = slea.readByte(); // 20 6E 4E
MapleMiniGame game = new MapleMiniGame(chr, desc);
chr.setMiniGame(game);
game.setPieceType(type);
game.setGameType("omok");
chr.getMap().addMapObject(game);
chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(chr, 1, 0));
game.sendOmok(c, type);
} else if (createType == 2) { // matchcard
if (chr.getChalkboard() != null) {
return;
}
String desc = slea.readMapleAsciiString();
slea.readByte(); // 20 6E 4E
int type = slea.readByte(); // 20 6E 4E
MapleMiniGame game = new MapleMiniGame(chr, desc);
game.setPieceType(type);
if (type == 0) {
game.setMatchesToWin(6);
} else if (type == 1) {
game.setMatchesToWin(10);
} else if (type == 2) {
game.setMatchesToWin(15);
}
game.setGameType("matchcard");
chr.setMiniGame(game);
chr.getMap().addMapObject(game);
chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(chr, 1, 0));
game.sendMatchCard(c, type);
} else if (createType == 4 || createType == 5) { // shop
if (!chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.SHOP, MapleMapObjectType.HIRED_MERCHANT)).isEmpty()) {
return;
}
String desc = slea.readMapleAsciiString();
slea.skip(3);
int itemId = slea.readInt();
if (chr.getInventory(MapleInventoryType.CASH).countById(itemId) < 1) {
return;
}
if (chr.getMapId() > 910000000 && chr.getMapId() < 910000023 || itemId > 5030000 && itemId < 5030012 || itemId > 5140000 && itemId < 5140006) {
if (createType == 4) {
MaplePlayerShop shop = new MaplePlayerShop(c.getPlayer(), desc);
chr.setPlayerShop(shop);
chr.getMap().addMapObject(shop);
shop.sendShop(c);
c.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(1));
} else {
HiredMerchant merchant = new HiredMerchant(chr, itemId, desc);
chr.setHiredMerchant(merchant);
chr.getClient().getChannelServer().addHiredMerchant(chr.getId(), merchant);
chr.announce(MaplePacketCreator.getHiredMerchant(chr, merchant, true));
}
}
}
} else if (mode == Action.INVITE.getCode()) {
int otherPlayer = slea.readInt();
MapleTrade.inviteTrade(chr, chr.getMap().getCharacterById(otherPlayer));
} else if (mode == Action.DECLINE.getCode()) {
MapleTrade.declineTrade(chr);
} else if (mode == Action.VISIT.getCode()) {
if (chr.getTrade() != null && chr.getTrade().getPartner() != null) {
MapleTrade.visitTrade(chr, chr.getTrade().getPartner().getChr());
} else {
int oid = slea.readInt();
MapleMapObject ob = chr.getMap().getMapObject(oid);
if (ob instanceof MaplePlayerShop) {
MaplePlayerShop shop = (MaplePlayerShop) ob;
if (shop.isBanned(chr.getName())) {
chr.dropMessage(1, "You have been banned from this store.");
return;
}
if (shop.hasFreeSlot() && !shop.isVisitor(c.getPlayer())) {
shop.addVisitor(c.getPlayer());
chr.setPlayerShop(shop);
shop.sendShop(c);
}
} else if (ob instanceof MapleMiniGame) {
MapleMiniGame game = (MapleMiniGame) ob;
if (game.hasFreeSlot() && !game.isVisitor(c.getPlayer())) {
game.addVisitor(c.getPlayer());
chr.setMiniGame(game);
if (game.getGameType().equals("omok")) {
game.sendOmok(c, game.getPieceType());
} else if (game.getGameType().equals("matchcard")) {
game.sendMatchCard(c, game.getPieceType());
}
} else {
chr.getClient().announce(MaplePacketCreator.getMiniGameFull());
}
} else if (ob instanceof HiredMerchant && chr.getHiredMerchant() == null) {
HiredMerchant merchant = (HiredMerchant) ob;
if (merchant.isOwner(c.getPlayer())) {
merchant.setOpen(false);
merchant.removeAllVisitors("");
c.announce(MaplePacketCreator.getHiredMerchant(chr, merchant, false));
} else if (!merchant.isOpen()) {
chr.dropMessage(1, "This shop is in maintenance, please come by later.");
return;
} else if (merchant.getFreeSlot() == -1) {
chr.dropMessage(1, "This shop has reached it's maximum capacity, please come by later.");
return;
} else {
merchant.addVisitor(c.getPlayer());
c.announce(MaplePacketCreator.getHiredMerchant(c.getPlayer(), merchant, false));
}
chr.setHiredMerchant(merchant);
}
}
} else if (mode == Action.CHAT.getCode()) { // chat lol
HiredMerchant merchant = chr.getHiredMerchant();
if (chr.getTrade() != null) {
chr.getTrade().chat(slea.readMapleAsciiString());
} else if (chr.getPlayerShop() != null) { // mini game
MaplePlayerShop shop = chr.getPlayerShop();
if (shop != null) {
shop.chat(c, slea.readMapleAsciiString());
}
} else if (chr.getMiniGame() != null) {
MapleMiniGame game = chr.getMiniGame();
if (game != null) {
game.chat(c, slea.readMapleAsciiString());
}
} else if (merchant != null) {
String message = chr.getName() + " : " + slea.readMapleAsciiString();
byte slot = (byte) (merchant.getVisitorSlot(c.getPlayer()) + 1);
merchant.getMessages().add(new HiredMerchantMessage(message, slot));
merchant.broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot));
}
} else if (mode == Action.EXIT.getCode()) {
if (chr.getTrade() != null) {
MapleTrade.cancelTrade(c.getPlayer());
} else {
MaplePlayerShop shop = chr.getPlayerShop();
MapleMiniGame game = chr.getMiniGame();
HiredMerchant merchant = chr.getHiredMerchant();
if (shop != null) {
if (shop.isOwner(c.getPlayer())) {
for (MaplePlayerShopItem mpsi : shop.getItems()) {
if (mpsi.getBundles() > 2) {
IItem iItem = mpsi.getItem().copy();
iItem.setQuantity((short) (mpsi.getBundles() * iItem.getQuantity()));
MapleInventoryManipulator.addFromDrop(c, iItem, false);
} else if (mpsi.isExist()) {
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), true);
}
}
chr.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(c.getPlayer()));
shop.removeVisitors();
} else {
shop.removeVisitor(c.getPlayer());
}
chr.setPlayerShop(null);
} else if (game != null) {
chr.setMiniGame(null);
if (game.isOwner(c.getPlayer())) {
chr.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(c.getPlayer()));
game.broadcastToVisitor(MaplePacketCreator.getMiniGameClose());
} else {
game.removeVisitor(c.getPlayer());
}
} else if (merchant != null) {
merchant.removeVisitor(c.getPlayer());
chr.setHiredMerchant(null);
}
}
} else if (mode == Action.OPEN.getCode()) {
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
if (shop != null && shop.isOwner(c.getPlayer())) {
slea.readByte();// 01
chr.getMap().broadcastMessage(MaplePacketCreator.addCharBox(c.getPlayer(), 4));
} else if (merchant != null && merchant.isOwner(c.getPlayer())) {
chr.setHasMerchant(true);
merchant.setOpen(true);
chr.getMap().addMapObject(merchant);
chr.setHiredMerchant(null);
chr.getMap().broadcastMessage(MaplePacketCreator.spawnHiredMerchant(merchant));
slea.readByte();
}
} else if (mode == Action.READY.getCode()) {
MapleMiniGame game = chr.getMiniGame();
game.broadcast(MaplePacketCreator.getMiniGameReady(game));
} else if (mode == Action.UN_READY.getCode()) {
MapleMiniGame game = chr.getMiniGame();
game.broadcast(MaplePacketCreator.getMiniGameUnReady(game));
} else if (mode == Action.START.getCode()) {
MapleMiniGame game = chr.getMiniGame();
if (game.getGameType().equals("omok")) {
game.broadcast(MaplePacketCreator.getMiniGameStart(game, game.getLoser()));
chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(game.getOwner(), 2, 1));
}
if (game.getGameType().equals("matchcard")) {
game.shuffleList();
game.broadcast(MaplePacketCreator.getMatchCardStart(game, game.getLoser()));
chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(game.getOwner(), 2, 1));
}
} else if (mode == Action.GIVE_UP.getCode()) {
MapleMiniGame game = chr.getMiniGame();
if (game.getGameType().equals("omok")) {
if (game.isOwner(c.getPlayer())) {
game.broadcast(MaplePacketCreator.getMiniGameOwnerForfeit(game));
} else {
game.broadcast(MaplePacketCreator.getMiniGameVisitorForfeit(game));
}
}
if (game.getGameType().equals("matchcard")) {
if (game.isOwner(c.getPlayer())) {
game.broadcast(MaplePacketCreator.getMatchCardVisitorWin(game));
} else {
game.broadcast(MaplePacketCreator.getMatchCardOwnerWin(game));
}
}
} else if (mode == Action.REQUEST_TIE.getCode()) {
MapleMiniGame game = chr.getMiniGame();
if (game.isOwner(c.getPlayer())) {
game.broadcastToVisitor(MaplePacketCreator.getMiniGameRequestTie(game));
} else {
game.getOwner().getClient().announce(MaplePacketCreator.getMiniGameRequestTie(game));
}
} else if (mode == Action.ANSWER_TIE.getCode()) {
MapleMiniGame game = chr.getMiniGame();
slea.readByte();
if (game.getGameType().equals("omok")) {
game.broadcast(MaplePacketCreator.getMiniGameTie(game));
}
if (game.getGameType().equals("matchcard")) {
game.broadcast(MaplePacketCreator.getMatchCardTie(game));
}
} else if (mode == Action.SKIP.getCode()) {
MapleMiniGame game = chr.getMiniGame();
if (game.isOwner(c.getPlayer())) {
game.broadcast(MaplePacketCreator.getMiniGameSkipOwner(game));
} else {
game.broadcast(MaplePacketCreator.getMiniGameSkipVisitor(game));
}
} else if (mode == Action.MOVE_OMOK.getCode()) {
int x = slea.readInt(); // x point
int y = slea.readInt(); // y point
int type = slea.readByte(); // piece ( 1 or 2; Owner has one piece,
// visitor has another, it switches
// every game.)
chr.getMiniGame().setPiece(x, y, type, c.getPlayer());
} else if (mode == Action.SELECT_CARD.getCode()) {
int turn = slea.readByte(); // 1st turn = 1; 2nd turn = 0
int slot = slea.readByte(); // slot
MapleMiniGame game = chr.getMiniGame();
int firstslot = game.getFirstSlot();
if (turn == 1) {
game.setFirstSlot(slot);
if (game.isOwner(c.getPlayer())) {
game.broadcastToVisitor(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
} else {
game.getOwner().getClient().announce(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
}
} else if ((game.getCardId(firstslot + 1)) == (game.getCardId(slot + 1))) {
if (game.isOwner(c.getPlayer())) {
game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 2));
game.setOwnerPoints();
} else {
game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 3));
game.setVisitorPoints();
}
} else if (game.isOwner(c.getPlayer())) {
game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 0));
} else {
game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 1));
}
} else if (mode == Action.SET_MESO.getCode()) {
chr.getTrade().setMeso(slea.readInt());
} else if (mode == Action.SET_ITEMS.getCode()) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
IItem item = chr.getInventory(ivType).getItem((byte) slea.readShort());
short quantity = slea.readShort();
byte targetSlot = slea.readByte();
if (chr.getTrade() != null) {
if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargable(item.getItemId())) {
if (ii.isDropRestricted(item.getItemId())) { // ensure that
// undroppable
// items do
// not make
// it to the
// trade
// window
if (!((item.getFlag() & ItemConstants.KARMA) == ItemConstants.KARMA || (item.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
}
IItem tradeItem = item.copy();
if (ItemConstants.isRechargable(item.getItemId())) {
tradeItem.setQuantity(item.getQuantity());
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true);
} else {
tradeItem.setQuantity(quantity);
MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
}
tradeItem.setPosition(targetSlot);
chr.getTrade().addItem(tradeItem);
return;
}
}
} else if (mode == Action.CONFIRM.getCode()) {
MapleTrade.completeTrade(c.getPlayer());
} else if (mode == Action.ADD_ITEM.getCode() || mode == Action.PUT_ITEM.getCode()) {
MapleInventoryType type = MapleInventoryType.getByType(slea.readByte());
byte slot = (byte) slea.readShort();
short bundles = slea.readShort();
if (chr.getItemQuantity(chr.getInventory(type).getItem(slot).getItemId(), false) < bundles || chr.getInventory(type).getItem(slot).getFlag() == ItemConstants.UNTRADEABLE) {
return;
}
short perBundle = slea.readShort();
int price = slea.readInt();
if (perBundle < 0 || perBundle * bundles > 2000 || bundles < 0 || price < 0) {
return;
}
IItem ivItem = chr.getInventory(type).getItem(slot);
IItem sellItem = ivItem.copy();
if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) {
return;
}
sellItem.setQuantity(perBundle);
MaplePlayerShopItem item = new MaplePlayerShopItem(sellItem, bundles, price);
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
if (shop != null && shop.isOwner(c.getPlayer())) {
if (ivItem != null && ivItem.getQuantity() >= bundles * perBundle) {
shop.addItem(item);
c.announce(MaplePacketCreator.getPlayerShopItemUpdate(shop));
}
} else if (merchant != null && merchant.isOwner(c.getPlayer())) {
merchant.addItem(item);
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
}
if (ItemConstants.isRechargable(ivItem.getItemId())) {
MapleInventoryManipulator.removeFromSlot(c, type, slot, ivItem.getQuantity(), true);
} else {
MapleInventoryManipulator.removeFromSlot(c, type, slot, (short) (bundles * perBundle), true);
}
} else if (mode == Action.REMOVE_ITEM.getCode()) {
MaplePlayerShop shop = chr.getPlayerShop();
if (shop != null && shop.isOwner(c.getPlayer())) {
int slot = slea.readShort();
MaplePlayerShopItem item = shop.getItems().get(slot);
IItem ivItem = item.getItem().copy();
shop.removeItem(slot);
ivItem.setQuantity(item.getBundles());
MapleInventoryManipulator.addFromDrop(c, ivItem, false);
c.announce(MaplePacketCreator.getPlayerShopItemUpdate(shop));
}
} else if (mode == Action.MERCHANT_MESO.getCode()) {// Hmmmm
/*
* if (!chr.getHiredMerchant().isOwner(chr) || chr.getMerchantMeso()
* < 1) return; int possible = Integer.MAX_VALUE -
* chr.getMerchantMeso(); if (possible > 0) { if (possible <
* chr.getMerchantMeso()) { chr.gainMeso(possible, false);
* chr.setMerchantMeso(chr.getMerchantMeso() - possible); } else {
* chr.gainMeso(chr.getMerchantMeso(), false);
* chr.setMerchantMeso(0); }
* c.announce(MaplePacketCreator.updateHiredMerchant
* (chr.getHiredMerchant(), chr)); }
*/
} else if (mode == Action.MERCHANT_ORGANIZE.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
if (!merchant.isOwner(chr))
return;
if (chr.getMerchantMeso() > 0) {
int possible = Integer.MAX_VALUE - chr.getMerchantMeso();
if (possible > 0) {
if (possible < chr.getMerchantMeso()) {
chr.gainMeso(possible, false);
chr.setMerchantMeso(chr.getMerchantMeso() - possible);
} else {
chr.gainMeso(chr.getMerchantMeso(), false);
chr.setMerchantMeso(0);
}
}
}
for (int i = 0; i < merchant.getItems().size(); i++) {
if (!merchant.getItems().get(i).isExist())
merchant.removeFromSlot(i);
}
if (merchant.getItems().isEmpty()) {
c.announce(MaplePacketCreator.hiredMerchantOwnerLeave());
c.announce(MaplePacketCreator.leaveHiredMerchant(0x00, 0x03));
merchant.closeShop(c, false);
chr.setHasMerchant(false);
return;
}
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
} else if (mode == Action.BUY.getCode() || mode == Action.MERCHANT_BUY.getCode()) {
int item = slea.readByte();
short quantity = slea.readShort();
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.getOwner().equals(chr.getName())) {
return;
}
if (shop != null && shop.isVisitor(c.getPlayer())) {
shop.buy(c, item, quantity);
shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
} else if (merchant != null) {
merchant.buy(c, item, quantity);
merchant.broadcastToVisitors(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
}
} else if (mode == Action.TAKE_ITEM_BACK.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.isOwner(c.getPlayer())) {
int slot = slea.readShort();
MaplePlayerShopItem item = merchant.getItems().get(slot);
if (item.getBundles() > 0) {
IItem iitem = item.getItem();
iitem.setQuantity((short) (item.getItem().getQuantity() * item.getBundles()));
MapleInventoryManipulator.addFromDrop(c, iitem, true);
}
merchant.removeFromSlot(slot);
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
}
} else if (mode == Action.CLOSE_MERCHANT.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.isOwner(c.getPlayer())) {
c.announce(MaplePacketCreator.hiredMerchantOwnerLeave());
c.announce(MaplePacketCreator.leaveHiredMerchant(0x00, 0x03));
merchant.closeShop(c, false);
chr.setHasMerchant(false);
}
} else if (mode == Action.MAINTENANCE_OFF.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
if (merchant.getItems().isEmpty() && merchant.isOwner(c.getPlayer())) {
merchant.closeShop(c, false);
chr.setHasMerchant(false);
}
if (merchant != null && merchant.isOwner(c.getPlayer())) {
merchant.getMessages().clear();
merchant.setOpen(true);
}
chr.setHiredMerchant(null);
c.announce(MaplePacketCreator.enableActions());
} else if (mode == Action.BAN_PLAYER.getCode()) {
if (chr.getPlayerShop() != null && chr.getPlayerShop().isOwner(c.getPlayer())) {
chr.getPlayerShop().banPlayer(slea.readMapleAsciiString());
}
}
}
}