New Year cards + Info on PlayerShops/HiredMerchants

New Year is coming soon! Figured out implementation for the New Year cards. Opcodes and packet structures are thanks to Eric.
New Year effect crashes/dc's the player in some cases.
Added informative notes to shop owners when transactioning an item/bundle.
This commit is contained in:
ronancpl
2017-12-19 16:40:45 -02:00
parent c1126213cc
commit 012f965f6a
19 changed files with 799 additions and 56 deletions

View File

@@ -116,6 +116,7 @@ import client.inventory.MaplePet;
import client.inventory.MapleWeaponType;
import client.inventory.ModifyInventory;
import client.inventory.PetDataFactory;
import client.newyear.NewYearCardRecord;
import constants.ExpTable;
import constants.GameConstants;
import constants.ItemConstants;
@@ -238,6 +239,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private MonsterBook monsterbook;
private MapleRing marriageRing;
private CashShop cashshop;
private Set<NewYearCardRecord> newyears = new LinkedHashSet<>();
private SavedLocation savedLocations[];
private SkillMacro[] skillMacros = new SkillMacro[5];
private List<Integer> lastmonthfameids;
@@ -830,7 +832,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
announce(MaplePacketCreator.getGMEffect(0x10, (byte) 0));
List<MapleBuffStat> dsstat = Collections.singletonList(MapleBuffStat.DARKSIGHT);
getMap().broadcastGMMessage(this, MaplePacketCreator.cancelForeignBuff(id, dsstat), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapobject(this), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapObject(this), false);
for(MapleSummon ms: this.getSummonsValues()) {
getMap().broadcastNONGMMessage(this, MaplePacketCreator.spawnSummon(ms, false), false);
@@ -843,7 +845,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if (!login) {
getMap().broadcastMessage(this, MaplePacketCreator.removePlayerFromMap(getId()), false);
}
getMap().broadcastGMMessage(this, MaplePacketCreator.spawnPlayerMapobject(this), false);
getMap().broadcastGMMessage(this, MaplePacketCreator.spawnPlayerMapObject(this), false);
List<Pair<MapleBuffStat, Integer>> dsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0));
getMap().broadcastGMMessage(this, MaplePacketCreator.giveForeignBuff(id, dsstat), false);
for (MapleMonster mon : this.getControlledMonsters()) {
@@ -1074,7 +1076,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
guildUpdate();
getMap().broadcastMessage(this, MaplePacketCreator.removePlayerFromMap(this.getId()), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapobject(this), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapObject(this), false);
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 8), false);
if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) {
@@ -5338,6 +5340,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
NewYearCardRecord.loadPlayerNewYearCards(ret);
PreparedStatement ps2, ps3;
ResultSet rs2, rs3;
@@ -7571,7 +7575,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
@Override
public void sendSpawnData(MapleClient client) {
if (!this.isHidden() || client.getPlayer().gmLevel() > 1) {
client.announce(MaplePacketCreator.spawnPlayerMapobject(this));
client.announce(MaplePacketCreator.spawnPlayerMapObject(this));
}
if (this.isHidden()) {
@@ -7600,6 +7604,40 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public CashShop getCashShop() {
return cashshop;
}
public Set<NewYearCardRecord> getNewYearRecords() {
return newyears;
}
public Set<NewYearCardRecord> getReceivedNewYearRecords() {
Set<NewYearCardRecord> received = new LinkedHashSet<>();
for(NewYearCardRecord nyc : newyears) {
if(nyc.isReceiverCardReceived()) {
received.add(nyc);
}
}
return received;
}
public NewYearCardRecord getNewYearRecord(int cardid) {
for(NewYearCardRecord nyc : newyears) {
if(nyc.getId() == cardid) {
return nyc;
}
}
return null;
}
public void addNewYearRecord(NewYearCardRecord newyear) {
newyears.add(newyear);
}
public void removeNewYearRecord(NewYearCardRecord newyear) {
newyears.remove(newyear);
}
public void portalDelay(long delay) {
this.portaldelay = System.currentTimeMillis() + delay;

View File

@@ -0,0 +1,377 @@
/*
* This file is part of the HeavenMS (MapleSolaxiaV2) Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.newyear;
import client.MapleCharacter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import net.server.Server;
import server.TimerManager;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
/**
*
* @author Ronan (credits to Eric for showing the New Year opcodes and handler layout)
*/
public class NewYearCardRecord {
private int id;
private int senderId;
private String senderName;
private boolean senderDiscardCard;
private int receiverId;
private String receiverName;
private boolean receiverDiscardCard;
private boolean receiverReceivedCard;
private String stringContent;
private long dateSent = 0;
private long dateReceived = 0;
private ScheduledFuture<?> sendTask = null;
public NewYearCardRecord(int senderid, String sender, int receiverid, String receiver, String message) {
this.id = -1;
this.senderId = senderid;
this.senderName = sender;
this.senderDiscardCard = false;
this.receiverId = receiverid;
this.receiverName = receiver;
this.receiverDiscardCard = false;
this.receiverReceivedCard = false;
this.stringContent = message;
this.dateSent = System.currentTimeMillis();
this.dateReceived = 0;
}
private void setExtraNewYearCardRecord(int id, boolean senderDiscardCard, boolean receiverDiscardCard, boolean receiverReceivedCard, long dateSent, long dateReceived) {
this.id = id;
this.senderDiscardCard = senderDiscardCard;
this.receiverDiscardCard = receiverDiscardCard;
this.receiverReceivedCard = receiverReceivedCard;
this.dateSent = dateSent;
this.dateReceived = dateReceived;
}
public void setId(int cardid) {
this.id = cardid;
}
public int getId() {
return this.id;
}
public int getSenderId() {
return senderId;
}
public String getSenderName() {
return senderName;
}
public boolean isSenderCardDiscarded() {
return senderDiscardCard;
}
public int getReceiverId() {
return receiverId;
}
public String getReceiverName() {
return receiverName;
}
public boolean isReceiverCardDiscarded() {
return receiverDiscardCard;
}
public boolean isReceiverCardReceived() {
return receiverReceivedCard;
}
public String getMessage() {
return stringContent;
}
public long getDateSent() {
return dateSent;
}
public long getDateReceived() {
return dateReceived;
}
public static void saveNewYearCard(NewYearCardRecord newyear) {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("INSERT INTO newyear VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) {
ps.setInt(1, newyear.senderId);
ps.setString(2, newyear.senderName);
ps.setInt(3, newyear.receiverId);
ps.setString(4, newyear.receiverName);
ps.setString(5, newyear.stringContent);
ps.setBoolean(6, newyear.senderDiscardCard);
ps.setBoolean(7, newyear.receiverDiscardCard);
ps.setBoolean(8, newyear.receiverReceivedCard);
ps.setLong(9, newyear.dateSent);
ps.setLong(10, newyear.dateReceived);
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
rs.next();
newyear.id = rs.getInt(1);
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
public static void updateNewYearCard(NewYearCardRecord newyear) {
newyear.receiverReceivedCard = true;
newyear.dateReceived = System.currentTimeMillis();
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("UPDATE newyear SET received=1, timereceived=? WHERE id=?")) {
ps.setLong(1, newyear.dateReceived);
ps.setInt(2, newyear.id);
ps.executeUpdate();
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
public static NewYearCardRecord loadNewYearCard(int cardid) {
NewYearCardRecord nyc = Server.getInstance().getNewYearCard(cardid);
if(nyc != null) return nyc;
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM newyear WHERE id = ?")) {
ps.setInt(1, cardid);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
NewYearCardRecord newyear = new NewYearCardRecord(rs.getInt("senderid"), rs.getString("sendername"), rs.getInt("receiverid"), rs.getString("receivername"), rs.getString("message"));
newyear.setExtraNewYearCardRecord(rs.getInt("id"), rs.getBoolean("senderdiscard"), rs.getBoolean("receiverdiscard"), rs.getBoolean("received"), rs.getLong("timesent"), rs.getLong("timereceived"));
Server.getInstance().setNewYearCard(newyear);
return newyear;
}
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
return null;
}
public static void loadPlayerNewYearCards(MapleCharacter chr) {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM newyear WHERE senderid = ? OR receiverid = ?")) {
ps.setInt(1, chr.getId());
ps.setInt(2, chr.getId());
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
NewYearCardRecord newyear = new NewYearCardRecord(rs.getInt("senderid"), rs.getString("sendername"), rs.getInt("receiverid"), rs.getString("receivername"), rs.getString("message"));
newyear.setExtraNewYearCardRecord(rs.getInt("id"), rs.getBoolean("senderdiscard"), rs.getBoolean("receiverdiscard"), rs.getBoolean("received"), rs.getLong("timesent"), rs.getLong("timereceived"));
chr.addNewYearRecord(newyear);
}
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
public static void printNewYearRecords(MapleCharacter chr) {
chr.dropMessage(5, "New Years: " + chr.getNewYearRecords().size());
for(NewYearCardRecord nyc : chr.getNewYearRecords()) {
chr.dropMessage(5, "-------------------------------");
chr.dropMessage(5, "Id: " + nyc.id);
chr.dropMessage(5, "Sender id: " + nyc.senderId);
chr.dropMessage(5, "Sender name: " + nyc.senderName);
chr.dropMessage(5, "Sender discard: " + nyc.senderDiscardCard);
chr.dropMessage(5, "Receiver id: " + nyc.receiverId);
chr.dropMessage(5, "Receiver name: " + nyc.receiverName);
chr.dropMessage(5, "Receiver discard: " + nyc.receiverDiscardCard);
chr.dropMessage(5, "Received: " + nyc.receiverReceivedCard);
chr.dropMessage(5, "Message: " + nyc.stringContent);
chr.dropMessage(5, "Date sent: " + nyc.dateSent);
chr.dropMessage(5, "Date recv: " + nyc.dateReceived);
}
}
private static int getCharacterWorld(int receiverid) {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT world FROM characters WHERE id = ?")) {
ps.setInt(1, receiverid);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("world");
}
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
return -1;
}
public void startNewYearCardTask() {
if(sendTask != null) return;
sendTask = TimerManager.getInstance().register(new Runnable() {
@Override
public void run() {
int world = getCharacterWorld(receiverId);
if(world == -1) {
sendTask.cancel(false);
sendTask = null;
return;
}
MapleCharacter target = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(receiverId);
if(target != null && target.isLoggedin() && !target.isAwayFromWorld()) {
target.announce(MaplePacketCreator.onNewYearCardRes(target, NewYearCardRecord.this, 0xC, 0));
}
}
}, 1000 * 60 * 60); //1 Hour
}
public void stopNewYearCardTask() {
if(sendTask != null) {
sendTask.cancel(false);
sendTask = null;
}
}
private static void deleteNewYearCard(int id) {
Server.getInstance().removeNewYearCard(id);
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("DELETE FROM newyear WHERE id = ?")) {
ps.setInt(1, id);
ps.executeUpdate();
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
public static void removeAllNewYearCard(boolean send, MapleCharacter chr) {
int cid = chr.getId();
/* not truly needed since it's going to be hard removed from the DB
String actor = (send ? "sender" : "receiver");
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("UPDATE newyear SET " + actor + "id = 1, received = 0 WHERE " + actor + "id = ?")) {
ps.setInt(1, cid);
ps.executeUpdate();
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
*/
Set<NewYearCardRecord> set = new HashSet<>(chr.getNewYearRecords());
for(NewYearCardRecord nyc : set) {
if(send) {
if(nyc.senderId == cid) {
nyc.senderDiscardCard = true;
nyc.receiverReceivedCard = false;
chr.removeNewYearRecord(nyc);
deleteNewYearCard(nyc.id);
chr.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(chr, nyc, 0xE, 0));
MapleCharacter other = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(nyc.getReceiverId());
if(other != null && other.isLoggedin() && !other.isAwayFromWorld()) {
other.removeNewYearRecord(nyc);
other.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(other, nyc, 0xE, 0));
other.dropMessage(6, "[NEW YEAR] " + chr.getName() + " threw away the New Year card.");
}
}
} else {
if(nyc.receiverId == cid) {
nyc.receiverDiscardCard = true;
nyc.receiverReceivedCard = false;
chr.removeNewYearRecord(nyc);
deleteNewYearCard(nyc.id);
chr.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(chr, nyc, 0xE, 0));
MapleCharacter other = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(nyc.getSenderId());
if(other != null && other.isLoggedin() && !other.isAwayFromWorld()) {
other.removeNewYearRecord(nyc);
other.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(other, nyc, 0xE, 0));
other.dropMessage(6, "[NEW YEAR] " + chr.getName() + " threw away the New Year card.");
}
}
}
}
}
public static void startPendingNewYearCardRequests() {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM newyear WHERE timereceived = 0 AND senderdiscard = 0")) {
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
NewYearCardRecord newyear = new NewYearCardRecord(rs.getInt("senderid"), rs.getString("sendername"), rs.getInt("receiverid"), rs.getString("receivername"), rs.getString("message"));
newyear.setExtraNewYearCardRecord(rs.getInt("id"), rs.getBoolean("senderdiscard"), rs.getBoolean("receiverdiscard"), rs.getBoolean("received"), rs.getLong("timesent"), rs.getLong("timereceived"));
Server.getInstance().setNewYearCard(newyear);
newyear.startNewYearCardTask();
}
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
}

View File

@@ -89,6 +89,14 @@ public final class ItemConstants {
return itemId / 1000 == 5000;
}
public static boolean isNewYearCardEtc(int itemId) {
return itemId / 10000 == 430;
}
public static boolean isNewYearCardUse(int itemId) {
return itemId / 10000 == 216;
}
public static boolean isAccessory(int itemId) {
return itemId >= 1110000 && itemId < 1140000;
}

View File

@@ -226,6 +226,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.ALLIANCE_OPERATION, new AllianceOperationHandler());
registerHandler(RecvOpcode.USE_SOLOMON_ITEM, new UseSolomonHandler());
registerHandler(RecvOpcode.USE_GACHA_EXP, new UseGachaExpHandler());
registerHandler(RecvOpcode.NEW_YEAR_CARD_REQUEST, new NewYearCardHandler());
registerHandler(RecvOpcode.USE_ITEM_REWARD, new ItemRewardHandler());
registerHandler(RecvOpcode.USE_REMOTE, new RemoteGachaponHandler());
registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler());

View File

@@ -146,6 +146,7 @@ public enum RecvOpcode {
ENTER_MTS(0x9C),
USE_SOLOMON_ITEM(0x9D),
USE_GACHA_EXP(0x9E),
NEW_YEAR_CARD_REQUEST(0x9F),
CLICK_GUIDE(0xA2),
ARAN_COMBO_COUNTER(0xA3),
MOVE_PET(0xA7),

View File

@@ -68,6 +68,7 @@ import tools.Pair;
import client.MapleClient;
import client.MapleCharacter;
import client.SkillFactory;
import client.newyear.NewYearCardRecord;
import constants.ItemConstants;
import constants.ServerConstants;
import java.util.Calendar;
@@ -91,6 +92,7 @@ public class Server implements Runnable {
private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
private final Map<Integer, NewYearCardRecord> newyears = new HashMap<>();
private boolean online = false;
public static long uptime = System.currentTimeMillis();
@@ -109,6 +111,18 @@ public class Server implements Runnable {
public List<Pair<Integer, String>> worldRecommendedList() {
return worldRecommendedList;
}
public void setNewYearCard(NewYearCardRecord nyc) {
newyears.put(nyc.getId(), nyc);
}
public NewYearCardRecord getNewYearCard(int cardid) {
return newyears.get(cardid);
}
public NewYearCardRecord removeNewYearCard(int cardid) {
return newyears.remove(cardid);
}
/*
public void removeChannel(int worldid, int channel) { //lol don't!
@@ -306,6 +320,8 @@ public class Server implements Runnable {
MapleQuest.loadAllQuest();
System.out.println("Quest loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
NewYearCardRecord.startPendingNewYearCardRequests();
if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().registerThreadTrackerTask();
try {

View File

@@ -48,7 +48,7 @@ public final class GuildOperationHandler extends AbstractMaplePacketHandler {
private void respawnPlayer(MapleCharacter mc) {
mc.getMap().broadcastMessage(mc, MaplePacketCreator.removePlayerFromMap(mc.getId()), false);
mc.getMap().broadcastMessage(mc, MaplePacketCreator.spawnPlayerMapobject(mc), false);
mc.getMap().broadcastMessage(mc, MaplePacketCreator.spawnPlayerMapObject(mc), false);
}
private class Invited {

View File

@@ -0,0 +1,153 @@
/*
* This file is part of the HeavenMS (MapleSolaxiaV2) Maple Story Server
*
* Copyright (C) 2017 RonanLana
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.channel.handlers;
import constants.ItemConstants;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.newyear.NewYearCardRecord;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Ronan - header layout courtesy of Eric
*/
public final class NewYearCardHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
final MapleCharacter player = c.getPlayer();
byte reqMode = slea.readByte(); //[00] -> NewYearReq (0 = Send)
if(reqMode == 0) { // card has been sent
if(player.haveItem(2160101)) { // new year's card
short slot = slea.readShort(); //[00 2C] -> nPOS (Item Slot Pos)
int itemid = slea.readInt(); //[00 20 F5 E5] -> nItemID (item id)
int status = getValidNewYearCardStatus(itemid, player, slot);
if(status == 0) {
if(player.canHold(4300000, 1)) {
String receiver = slea.readMapleAsciiString(); //[04 00 54 65 73 74] -> sReceiverName (person to send to)
int receiverid = getReceiverId(receiver, c.getWorld());
if(receiverid != -1) {
if(receiverid != c.getPlayer().getId()) {
String message = slea.readMapleAsciiString(); //[06 00 4C 65 74 74 65 72] -> sContent (message)
NewYearCardRecord newyear = new NewYearCardRecord(player.getId(), player.getName(), receiverid, receiver, message);
NewYearCardRecord.saveNewYearCard(newyear);
player.addNewYearRecord(newyear);
player.getClient().getAbstractPlayerInteraction().gainItem(2160101, (short)-1);
player.getClient().getAbstractPlayerInteraction().gainItem(4300000, (short) 1);
Server.getInstance().setNewYearCard(newyear);
newyear.startNewYearCardTask();
player.announce(MaplePacketCreator.onNewYearCardRes(player, newyear, 4, 0)); // successfully sent
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, 0xF)); // cannot send to yourself
}
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, 0x13)); // cannot find such character
}
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, 0x10)); // inventory full
}
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, status)); // item and inventory errors
}
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, 0x11)); // have no card to send
}
} else { //receiver accepted the card
int cardid = slea.readInt();
NewYearCardRecord newyear = NewYearCardRecord.loadNewYearCard(cardid);
if(newyear != null && newyear.getReceiverId() == player.getId() && !newyear.isReceiverCardReceived()) {
if(!newyear.isSenderCardDiscarded()) {
if(player.canHold(4301000, 1)) {
newyear.stopNewYearCardTask();
NewYearCardRecord.updateNewYearCard(newyear);
player.getClient().getAbstractPlayerInteraction().gainItem(4301000, (short)1);
if(!newyear.getMessage().isEmpty()) player.dropMessage(6, "[NEW YEAR] " + newyear.getSenderName() + ": " + newyear.getMessage());
player.addNewYearRecord(newyear);
player.announce(MaplePacketCreator.onNewYearCardRes(player, newyear, 6, 0)); // successfully rcvd
player.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(player, newyear, 0xD, 0));
MapleCharacter sender = c.getWorldServer().getPlayerStorage().getCharacterById(newyear.getSenderId());
if(sender != null && sender.isLoggedin() && !sender.isAwayFromWorld()) {
sender.getMap().broadcastMessage(MaplePacketCreator.onNewYearCardRes(sender, newyear, 0xD, 0));
sender.dropMessage(6, "[NEW YEAR] Your addressee successfully received the New Year card.");
}
} else {
player.announce(MaplePacketCreator.onNewYearCardRes(player, -1, 5, 0x10)); // inventory full
}
} else {
player.dropMessage(6, "[NEW YEAR] The sender of the New Year card already dropped it. Nothing to receive.");
}
} else {
if(newyear == null) {
player.dropMessage(6, "[NEW YEAR] The sender of the New Year card already dropped it. Nothing to receive.");
}
}
}
}
private static int getReceiverId(String receiver, int world) {
try (Connection con = DatabaseConnection.getConnection()) {
try (PreparedStatement ps = con.prepareStatement("SELECT id, world FROM characters WHERE name LIKE ?")) {
ps.setString(1, receiver);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
if(rs.getInt("world") == world) {
return rs.getInt("id");
}
}
}
}
} catch(SQLException sqle) {
sqle.printStackTrace();
}
return -1;
}
private static int getValidNewYearCardStatus(int itemid, MapleCharacter player, short slot) {
if(!ItemConstants.isNewYearCardUse(itemid)) return 0x14;
Item it = player.getInventory(MapleItemInformationProvider.getInstance().getInventoryType(itemid)).getItem(slot);
return (it != null && it.getItemId() == itemid) ? 0 : 0x12;
}
}

View File

@@ -336,7 +336,7 @@ public class World {
}
if (bDifferentGuild) {
mc.getMap().broadcastMessage(mc, MaplePacketCreator.removePlayerFromMap(cid), false);
mc.getMap().broadcastMessage(mc, MaplePacketCreator.spawnPlayerMapobject(mc), false);
mc.getMap().broadcastMessage(mc, MaplePacketCreator.spawnPlayerMapObject(mc), false);
}
}

View File

@@ -28,6 +28,7 @@ import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.ModifyInventory;
import client.newyear.NewYearCardRecord;
import constants.ItemConstants;
import constants.ServerConstants;
@@ -544,7 +545,7 @@ public class MapleInventoryManipulator {
c.getPlayer().setChalkboard(null);
}
}
if (source == null || (!ItemConstants.isRechargable(itemId) && source.getQuantity() < quantity) || quantity < 0) {
if ((!ItemConstants.isRechargable(itemId) && source.getQuantity() < quantity) || quantity < 0) {
return;
}
Point dropPos = new Point(c.getPlayer().getPosition());
@@ -553,6 +554,17 @@ public class MapleInventoryManipulator {
target.setQuantity(quantity);
source.setQuantity((short) (source.getQuantity() - quantity));
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, source))));
if(ItemConstants.isNewYearCardEtc(itemId)) {
if(itemId == 4300000) {
NewYearCardRecord.removeAllNewYearCard(true, c.getPlayer());
c.getAbstractPlayerInteraction().removeAll(4300000);
} else {
NewYearCardRecord.removeAllNewYearCard(false, c.getPlayer());
c.getAbstractPlayerInteraction().removeAll(4301000);
}
}
boolean weddingRing = source.getItemId() == 1112803 || source.getItemId() == 1112806 || source.getItemId() == 1112807 || source.getItemId() == 1112809;
if (weddingRing) {
c.getPlayer().getMap().disappearingItemDrop(c.getPlayer(), c.getPlayer(), target, dropPos);
@@ -572,7 +584,16 @@ public class MapleInventoryManipulator {
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(3, source))));
if (src < 0) {
c.getPlayer().equipChanged();
} else if(ItemConstants.isNewYearCardEtc(itemId)) {
if(itemId == 4300000) {
NewYearCardRecord.removeAllNewYearCard(true, c.getPlayer());
c.getAbstractPlayerInteraction().removeAll(4300000);
} else {
NewYearCardRecord.removeAllNewYearCard(false, c.getPlayer());
c.getAbstractPlayerInteraction().removeAll(4301000);
}
}
if (c.getPlayer().getMap().getEverlast()) {
if (ii.isDropRestricted(itemId) || ii.isCash(itemId) || isDroppedItemRestricted(source)) {
c.getPlayer().getMap().disappearingItemDrop(c.getPlayer(), c.getPlayer(), source, dropPos);

View File

@@ -753,7 +753,7 @@ public class MapleStatEffect {
applyto.setStance(0);
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.removePlayerFromMap(applyto.getId()), false);
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.spawnPlayerMapobject(applyto), false);
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.spawnPlayerMapObject(applyto), false);
}
}
if (isDispel() && makeChanceResult()) {
@@ -919,7 +919,7 @@ public class MapleStatEffect {
applyto.removeAllCooldownsExcept(Buccaneer.TIME_LEAP, true);
} else if(isHyperBody() && !primary) {
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.removePlayerFromMap(applyto.getId()), false);
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.spawnPlayerMapobject(applyto), false);
applyto.getMap().broadcastMessage(applyto, MaplePacketCreator.spawnPlayerMapObject(applyto), false);
}
return true;

View File

@@ -182,11 +182,13 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
c.announce(MaplePacketCreator.enableActions());
return;
}
int price = (int)Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE);
int price = (int) Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE);
if (c.getPlayer().getMeso() >= price) {
if (MapleInventoryManipulator.checkSpace(c, newItem.getItemId(), newItem.getQuantity(), newItem.getOwner())) {
MapleInventoryManipulator.addFromDrop(c, newItem, false);
c.getPlayer().gainMeso(-price, false);
announceItemSold(newItem, price); // idea thanks to vcoc
synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price));
@@ -226,6 +228,16 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
}
}
}
private void announceItemSold(Item item, int mesos) {
String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : "";
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleCharacter player = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(ownerId);
if(player != null && player.isLoggedin() && !player.isAwayFromWorld()) {
player.dropMessage(6, "[HIRED MERCHANT] Item '" + ii.getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos.");
}
}
public void forceClose() {
Server.getInstance().getWorld(world).unregisterHiredMerchant(this);
@@ -508,7 +520,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
}
return true;
}
public int getChannel() {
return channel;
}

View File

@@ -2131,13 +2131,13 @@ public class MapleMap {
}
}
if (chr.isHidden()) {
broadcastGMMessage(chr, MaplePacketCreator.spawnPlayerMapobject(chr), false);
broadcastGMMessage(chr, MaplePacketCreator.spawnPlayerMapObject(chr), false);
chr.announce(MaplePacketCreator.getGMEffect(0x10, (byte) 1));
List<Pair<MapleBuffStat, Integer>> dsstat = Collections.singletonList(new Pair<MapleBuffStat, Integer>(MapleBuffStat.DARKSIGHT, 0));
broadcastGMMessage(chr, MaplePacketCreator.giveForeignBuff(chr.getId(), dsstat), false);
} else {
broadcastMessage(chr, MaplePacketCreator.spawnPlayerMapobject(chr), false);
broadcastMessage(chr, MaplePacketCreator.spawnPlayerMapObject(chr), false);
}
sendObjectPlacement(chr.getClient());

View File

@@ -34,9 +34,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import tools.locks.MonitoredReentrantLock;
import net.SendOpcode;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.maps.AbstractMapleMapObject;
import server.maps.MapleMapObjectType;
import server.MapleItemInformationProvider;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.output.MaplePacketLittleEndianWriter;
@@ -103,7 +103,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
visitor.setSlot(i);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, i + 1));
if(i == 2) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(this.getOwner(), 1));
if(i == 2) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(owner, 1));
break;
}
}
@@ -162,7 +162,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
visitorLock.unlock();
}
if(this.getOwner().getPlayerShop() != null) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(this.getOwner(), 4));
if(owner.getPlayerShop() != null) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(owner, 4));
}
}
@@ -205,10 +205,15 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
return;
}
synchronized (c.getPlayer()) {
if (c.getPlayer().getMeso() >= (long) pItem.getPrice() * quantity) {
int price = (int) Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE);
if (c.getPlayer().getMeso() >= price) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, false)) {
c.getPlayer().gainMeso(-pItem.getPrice() * quantity, false);
owner.gainMeso(pItem.getPrice() * quantity, true);
c.getPlayer().gainMeso(-price, false);
announceItemSold(newItem, price); // idea thanks to vcoc
owner.gainMeso(price, true);
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
@@ -227,6 +232,13 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
}
}
}
private void announceItemSold(Item item, int mesos) {
String qtyStr = (item.getQuantity() > 1) ? " (qty. " + item.getQuantity() + ")" : "";
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
owner.dropMessage(6, "[PLAYER SHOP] Item '" + ii.getName(item.getItemId()) + "'" + qtyStr + " has been sold for " + mesos + " mesos.");
}
public void broadcastToVisitors(final byte[] packet) {
visitorLock.lock();
@@ -283,7 +295,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
for(MapleCharacter mc : visitorList) forceRemoveVisitor(mc);
if (owner != null) {
forceRemoveVisitor(getOwner());
forceRemoveVisitor(owner);
}
}
@@ -476,12 +488,12 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
@Override
public void sendDestroyData(MapleClient client) {
client.announce(MaplePacketCreator.removeCharBox(this.getOwner()));
client.announce(MaplePacketCreator.removeCharBox(owner));
}
@Override
public void sendSpawnData(MapleClient client) {
client.announce(MaplePacketCreator.addCharBox(this.getOwner(), 4));
client.announce(MaplePacketCreator.addCharBox(owner, 4));
}
@Override

View File

@@ -33,6 +33,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.SendOpcode;
import net.server.PlayerCoolDownValueHolder;
@@ -98,6 +99,7 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import client.inventory.ModifyInventory;
import client.newyear.NewYearCardRecord;
import client.status.MonsterStatus;
import client.status.MonsterStatusEffect;
import constants.GameConstants;
@@ -217,30 +219,18 @@ public class MaplePacketCreator {
addRingInfo(mplew, chr);
addTeleportInfo(mplew, chr);
addMonsterBookInfo(mplew, chr);
addNewYearInfo(mplew, chr);//have fun!
addNewYearInfo(mplew, chr);
addAreaInfo(mplew, chr);//assuming it stayed here xd
mplew.writeShort(0);
}
private static void addNewYearInfo(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
mplew.writeShort(0);
/*
*(_DWORD *)this = CInPacket::Decode4(a2);
*((_DWORD *)v2 + 1) = CInPacket::Decode4(a2);
CInPacket::DecodeStr(&v7);
v9 = 0;
(*(void (__stdcall **)(char *, int))((char *)&loc_B1410B + 1))((char *)v2 + 8, v7);
*(_DWORD *)((char *)v2 + 21) = (unsigned __int8)CInPacket::Decode1(a2);
CInPacket::DecodeBuffer((char *)v2 + 25, 8);
*(_DWORD *)((char *)v2 + 33) = CInPacket::Decode4(a2);
CInPacket::DecodeStr(&v6);
LOBYTE(v8) = 1;
(*(void (__stdcall **)(char *, int))((char *)&loc_B1410B + 1))((char *)v2 + 37, v6);
*(_DWORD *)((char *)v2 + 50) = (unsigned __int8)CInPacket::Decode1(a2);
*(_DWORD *)((char *)v2 + 54) = (unsigned __int8)CInPacket::Decode1(a2);
CInPacket::DecodeBuffer((char *)v2 + 58, 8);
CInPacket::DecodeStr(&v9);
*/
Set<NewYearCardRecord> received = chr.getReceivedNewYearRecords();
mplew.writeShort(received.size());
for(NewYearCardRecord nyc : received) {
encodeNewYearCard(nyc, mplew);
}
}
private static void addTeleportInfo(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
@@ -1742,7 +1732,7 @@ public class MaplePacketCreator {
* @param chr The character to spawn to other clients.
* @return The spawn player packet.
*/
public static byte[] spawnPlayerMapobject(MapleCharacter chr) {
public static byte[] spawnPlayerMapObject(MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SPAWN_PLAYER.getValue());
mplew.writeInt(chr.getId());
@@ -1834,7 +1824,7 @@ public class MaplePacketCreator {
mplew.writeShort(chr.getJob().getId());
/* replace "mplew.writeShort(chr.getJob().getId())" with this snippet for 3rd party FJ animation on all classes
/* replace "mplew.writeShort(chr.getJob().getId())" with this snippet for 3rd person FJ animation on all classes
if (chr.getJob().isA(MapleJob.HERMIT) || chr.getJob().isA(MapleJob.DAWNWARRIOR2) || chr.getJob().isA(MapleJob.NIGHTWALKER2)) {
mplew.writeShort(chr.getJob().getId());
} else {
@@ -1887,14 +1877,106 @@ public class MaplePacketCreator {
} else {
mplew.write(0);
}
addRingLook(mplew, chr, true);
addRingLook(mplew, chr, false);
addRingLook(mplew, chr, true); // crush
addRingLook(mplew, chr, false); // friendship
addMarriageRingLook(mplew, chr);
mplew.skip(3);
encodeNewYearCardInfo(mplew, chr); // new year seems to crash sometimes...
mplew.skip(2);
mplew.write(chr.getTeam());//only needed in specific fields
return mplew.getPacket();
}
private static void encodeNewYearCardInfo(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
Set<NewYearCardRecord> newyears = chr.getReceivedNewYearRecords();
if(!newyears.isEmpty()) {
mplew.write(1);
mplew.writeInt(newyears.size());
for(NewYearCardRecord nyc : newyears) {
mplew.writeInt(nyc.getId());
}
} else {
mplew.write(0);
}
}
public static byte[] onNewYearCardRes(MapleCharacter user, int cardId, int mode, int msg) {
NewYearCardRecord newyear = user.getNewYearRecord(cardId);
return onNewYearCardRes(user, newyear, mode, msg);
}
public static byte[] onNewYearCardRes(MapleCharacter user, NewYearCardRecord newyear, int mode, int msg) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.NEW_YEAR_CARD_RES.getValue());
mplew.write(mode);
switch (mode) {
case 4: // Successfully sent a New Year Card\r\n to %s.
case 6: // Successfully received a New Year Card.
encodeNewYearCard(newyear, mplew);
break;
case 8: // Successfully deleted a New Year Card.
mplew.writeInt(newyear.getId());
break;
case 5: // Nexon's stupid and makes 4 modes do the same operation..
case 7:
case 9:
case 0xB:
// 0x10: You have no free slot to store card.\r\ntry later on please.
// 0x11: You have no card to send.
// 0x12: Wrong inventory information !
// 0x13: Cannot find such character !
// 0x14: Incoherent Data !
// 0x15: An error occured during DB operation.
// 0x16: An unknown error occured !
// 0xF: You cannot send a card to yourself !
mplew.write(msg);
break;
case 0xA: // GetUnreceivedList_Done
int nSN = 1;
mplew.writeInt(nSN);
if ((nSN - 1) <= 98 && nSN > 0) {//lol nexon are you kidding
for (int i = 0; i < nSN; i++) {
mplew.writeInt(newyear.getId());
mplew.writeInt(newyear.getSenderId());
mplew.writeMapleAsciiString(newyear.getSenderName());
}
}
break;
case 0xC: // NotiArrived
mplew.writeInt(newyear.getId());
mplew.writeMapleAsciiString(newyear.getSenderName());
break;
case 0xD: // BroadCast_AddCardInfo
mplew.writeInt(newyear.getId());
mplew.writeInt(user.getId());
break;
case 0xE: // BroadCast_RemoveCardInfo
mplew.writeInt(newyear.getId());
break;
}
return mplew.getPacket();
}
private static void encodeNewYearCard(NewYearCardRecord newyear, MaplePacketLittleEndianWriter mplew) {
mplew.writeInt(newyear.getId());
mplew.writeInt(newyear.getSenderId());
mplew.writeMapleAsciiString(newyear.getSenderName());
mplew.writeBool(newyear.isSenderCardDiscarded());
mplew.writeLong(newyear.getDateSent());
mplew.writeInt(newyear.getReceiverId());
mplew.writeMapleAsciiString(newyear.getReceiverName());
mplew.writeBool(newyear.isReceiverCardDiscarded());
mplew.writeBool(newyear.isReceiverCardReceived());
mplew.writeLong(newyear.getDateReceived());
mplew.writeMapleAsciiString(newyear.getMessage());
}
private static void addRingLook(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean crush) {
List<MapleRing> rings;
if (crush) {
@@ -1922,15 +2004,17 @@ public class MaplePacketCreator {
}
private static void addMarriageRingLook(final MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
if (chr.getMarriageRing() != null && !chr.getMarriageRing().equipped()) {
MapleRing ring = chr.getMarriageRing();
if (ring != null && !ring.equipped()) {
mplew.write(0);
return;
}
mplew.writeBool(chr.getMarriageRing() != null);
if (chr.getMarriageRing() != null) {
mplew.writeBool(ring != null);
if (ring != null) {
mplew.writeInt(chr.getId());
mplew.writeInt(chr.getMarriageRing().getPartnerChrId());
mplew.writeInt(chr.getMarriageRing().getRingId());
mplew.writeInt(ring.getPartnerChrId());
mplew.writeInt(ring.getRingId());
}
}