Hired Merchant & Player shop fixes + Owl of Minerva

Thanks to BenjixD DietStory's dev team, many bugs found by them have
been patched (pirate quests, missing drop data, fly command, etc).
Implemented Owl of Minerva. Fixed many issues with Hired Merchant and
Player Shops. Fixed an error with the slot checking system. Added Wish
Tickets (circa-2006 GMS event) to be dropped on AmoriaPQ.
This commit is contained in:
ronancpl
2017-09-29 18:35:42 -03:00
parent de7e686a92
commit ddbd679f88
129 changed files with 4564 additions and 400 deletions

View File

@@ -84,7 +84,7 @@ import server.events.gm.MapleOla;
import server.life.MapleMonster;
import server.life.MobSkill;
import server.maps.AbstractAnimatedMapleMapObject;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import server.maps.MapleDoor;
import server.maps.MapleDragon;
import server.maps.MapleMap;
@@ -152,6 +152,7 @@ import server.maps.MapleMapItem;
public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private static NumberFormat nf = new DecimalFormat("#,###,###,###");
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
private static final String LEVEL_200 = "[Congrats] %s has reached Level 200! Congratulate %s on such an amazing achievement!";
private static final String[] BLOCKED_NAMES = {"admin", "owner", "moderator", "intern", "donor", "administrator", "help", "helper", "alert", "notice", "maplestory", "Solaxia", "fuck", "wizet", "fucking", "negro", "fuk", "fuc", "penis", "pussy", "asshole", "gay",
"nigger", "homo", "suck", "cum", "shit", "shitty", "condom", "security", "official", "rape", "nigga", "sex", "tit", "boner", "orgy", "clit", "asshole", "fatass", "bitch", "support", "gamemaster", "cock", "gaay", "gm",
@@ -190,6 +191,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private int warpToId;
private int expRate = 1, mesoRate = 1, dropRate = 1, expCoupon = 1, mesoCoupon = 1, dropCoupon = 1;
private int omokwins, omokties, omoklosses, matchcardwins, matchcardties, matchcardlosses;
private int owlSearch;
private int married;
private long lastfametime, lastUsedCashItem, lastHealed, lastMesoDrop = -1, jailExpiration = -1;
private transient int localmaxhp, localmaxmp, localstr, localdex, localluk, localint_, magic, watk;
@@ -208,7 +210,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private int merchantmeso;
private BuddyList buddylist;
private EventInstanceManager eventInstance = null;
private HiredMerchant hiredMerchant = null;
private MapleHiredMerchant hiredMerchant = null;
private MapleClient client;
private MapleGuildCharacter mgc = null;
private MaplePartyCharacter mpc = null;
@@ -427,6 +429,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public void setNpcCooldown(long d) {
npcCd = d;
}
public void setOwlSearch(int id) {
owlSearch = id;
}
public int getOwlSearch() {
return owlSearch;
}
public void addCooldown(int skillId, long startTime, long length) {
effLock.lock();
@@ -699,7 +709,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
int maxbasedamage;
Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
if (weapon_item != null) {
MapleWeaponType weapon = MapleItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId());
MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
int mainstat, secondarystat;
if (getJob().isA(MapleJob.THIEF) && weapon == MapleWeaponType.DAGGER_OTHER) {
weapon = MapleWeaponType.DAGGER_THIEVES;
@@ -787,7 +797,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
String medal = "";
final Item medalItem = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49);
if (medalItem != null) {
medal = "<" + MapleItemInformationProvider.getInstance().getName(medalItem.getItemId()) + "> ";
medal = "<" + ii.getName(medalItem.getItemId()) + "> ";
}
return medal;
}
@@ -1342,7 +1352,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private static boolean useItem(final MapleClient c, final int id) {
if (id / 1000000 == 2) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (ii.isConsumeOnPickup(id)) {
if (ItemConstants.isPartyItem(id)) {
List<MapleCharacter> pchr = c.getPlayer().getPartyMembersOnSameMap();
@@ -1396,7 +1405,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
final byte[] pickupPacket = MaplePacketCreator.removeItemFromMap(mapitem.getObjectId(), (isPet) ? 5 : 2, this.getId(), isPet, petIndex);
boolean hasSpaceInventory = true;
if (mapitem.getItemId() == 4031865 || mapitem.getItemId() == 4031866 || mapitem.getMeso() > 0 || MapleItemInformationProvider.getInstance().isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = MapleInventoryManipulator.checkSpace(client, mapitem.getItemId(), mapitem.getItem().getQuantity(), mapitem.getItem().getOwner()))) {
if (mapitem.getItemId() == 4031865 || mapitem.getItemId() == 4031866 || mapitem.getMeso() > 0 || ii.isConsumeOnPickup(mapitem.getItemId()) || (hasSpaceInventory = MapleInventoryManipulator.checkSpace(client, mapitem.getItemId(), mapitem.getItem().getQuantity(), mapitem.getItem().getOwner()))) {
if ((this.getMapId() > 209000000 && this.getMapId() < 209000016) || (this.getMapId() >= 990000500 && this.getMapId() <= 990000502)) {//happyville trees and guild PQ
if (!mapitem.isPlayerDrop() || mapitem.getDropper().getObjectId() == client.getPlayer().getObjectId()) {
if(mapitem.getMeso() > 0) {
@@ -1460,7 +1469,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
this.gainMeso(mapitem.getMeso(), true, true, false);
}
} else if (mapitem.getItem().getItemId() / 10000 == 243) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleItemInformationProvider.scriptedItem info = ii.getScriptedItemInfo(mapitem.getItem().getItemId());
if (info.runOnPickup()) {
ItemScriptManager ism = ItemScriptManager.getInstance();
@@ -1504,7 +1512,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public int countItem(int itemid) {
return inventory[MapleItemInformationProvider.getInstance().getInventoryType(itemid).ordinal()].countById(itemid);
return inventory[ii.getInventoryType(itemid).ordinal()].countById(itemid);
}
public boolean canHold(int itemid) {
@@ -1512,12 +1520,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public boolean canHold(int itemid, int quantity) {
if(haveItem(itemid)) {
if(getItemQuantity(itemid, false) + quantity <= MapleItemInformationProvider.getInstance().getSlotMax(client, itemid))
int hold = getCleanItemQuantity(itemid, false);
if(hold > 0) {
if(hold + quantity <= ii.getSlotMax(client, itemid))
return true;
}
return getInventory(MapleItemInformationProvider.getInstance().getInventoryType(itemid)).getNextFreeSlot() > -1;
return getInventory(ii.getInventoryType(itemid)).getNextFreeSlot() > -1;
}
public void decreaseBattleshipHp(int decrease) {
@@ -2824,7 +2834,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public void cancelEffect(int itemId) {
cancelEffect(MapleItemInformationProvider.getInstance().getItemEffect(itemId), false, -1);
cancelEffect(ii.getItemEffect(itemId), false, -1);
}
public void cancelEffect(MapleStatEffect effect, boolean overwrite, long startTime) {
@@ -3534,7 +3544,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return hair;
}
public HiredMerchant getHiredMerchant() {
public MapleHiredMerchant getHiredMerchant() {
return hiredMerchant;
}
@@ -3640,12 +3650,20 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public int getItemQuantity(int itemid, boolean checkEquipped) {
int possesed = inventory[MapleItemInformationProvider.getInstance().getInventoryType(itemid).ordinal()].countById(itemid);
int possesed = inventory[ii.getInventoryType(itemid).ordinal()].countById(itemid);
if (checkEquipped) {
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countById(itemid);
}
return possesed;
}
public int getCleanItemQuantity(int itemid, boolean checkEquipped) {
int possesed = inventory[ii.getInventoryType(itemid).ordinal()].countCleanById(itemid);
if (checkEquipped) {
possesed += inventory[MapleInventoryType.EQUIPPED.ordinal()].countCleanById(itemid);
}
return possesed;
}
public MapleJob getJob() {
return job;
@@ -3967,6 +3985,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if(mps == null) return;
if (mps.isOwner(this)) {
mps.setOpen(false);
client.getWorldServer().unregisterPlayerShop(mps);
for (MaplePlayerShopItem mpsi : mps.getItems()) {
if (mpsi.getBundles() >= 2) {
Item iItem = mpsi.getItem().copy();
@@ -3976,8 +3997,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
MapleInventoryManipulator.addFromDrop(this.getClient(), mpsi.getItem(), true);
}
}
this.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(this));
mps.removeVisitors();
mps.closeShop();
} else {
mps.removeVisitor(this);
}
@@ -3998,7 +4018,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public void closeHiredMerchant(boolean closeMerchant) {
HiredMerchant merchant = this.getHiredMerchant();
MapleHiredMerchant merchant = this.getHiredMerchant();
if(merchant == null) return;
if(closeMerchant) {
@@ -4488,8 +4508,12 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return getItemQuantity(itemid, false) > 0;
}
public boolean haveCleanItem(int itemid) {
return getCleanItemQuantity(itemid, false) > 0;
}
public boolean hasEmptySlot(int itemId) {
return getInventory(MapleItemInformationProvider.getInstance().getInventoryType(itemId)).getNextFreeSlot() > -1;
return getInventory(ii.getInventoryType(itemId)).getNextFreeSlot() > -1;
}
public boolean hasEmptySlot(byte invType) {
@@ -4983,7 +5007,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private void commitBuffCoupon(int couponid) {
if(!isLoggedin() || getCashShop().isOpened()) return;
MapleStatEffect mse = MapleItemInformationProvider.getInstance().getItemEffect(couponid);
MapleStatEffect mse = ii.getItemEffect(couponid);
mse.applyTo(this);
}
@@ -5536,7 +5560,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
if (possesed > 0) {
message("You have used a safety charm, so your EXP points have not been decreased.");
MapleInventoryManipulator.removeById(client, MapleItemInformationProvider.getInstance().getInventoryType(charmID[i]), charmID[i], 1, true, false);
MapleInventoryManipulator.removeById(client, ii.getInventoryType(charmID[i]), charmID[i], 1, true, false);
} else if (mapid > 925020000 && mapid < 925030000) {
this.dojoStage = 0;
} else if (mapid > 980000100 && mapid < 980000700) {
@@ -5686,7 +5710,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.NIGHTWALKER1) || job.isA(MapleJob.WINDARCHER1)) {
Item weapon_item = getInventory(MapleInventoryType.EQUIPPED).getItem((short) -11);
if (weapon_item != null) {
MapleWeaponType weapon = MapleItemInformationProvider.getInstance().getWeaponType(weapon_item.getItemId());
MapleWeaponType weapon = ii.getWeaponType(weapon_item.getItemId());
boolean bow = weapon == MapleWeaponType.BOW;
boolean crossbow = weapon == MapleWeaponType.CROSSBOW;
boolean claw = weapon == MapleWeaponType.CLAW;
@@ -5700,7 +5724,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
if ((claw && ItemConstants.isThrowingStar(item.getItemId())) || (gun && ItemConstants.isBullet(item.getItemId())) || (bow && ItemConstants.isArrowForBow(item.getItemId())) || (crossbow && ItemConstants.isArrowForCrossBow(item.getItemId()))) {
if (item.getQuantity() > 0) {
// Finally there!
watk += MapleItemInformationProvider.getInstance().getWatkForProjectile(item.getItemId());
watk += ii.getWatkForProjectile(item.getItemId());
break;
}
}
@@ -6543,7 +6567,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
try {
newAmount = (int)Math.min((long)merchantmeso + add, Integer.MAX_VALUE);
System.out.println("adding" + add + " now" + newAmount);
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("UPDATE characters SET MerchantMesos = ? WHERE id = ?", Statement.RETURN_GENERATED_KEYS)) {
@@ -6576,7 +6599,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
merchantmeso = set;
}
public void setHiredMerchant(HiredMerchant merchant) {
public void setHiredMerchant(MapleHiredMerchant merchant) {
this.hiredMerchant = merchant;
}
@@ -6822,7 +6845,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public int sellAllItemsFromName(byte invTypeId, String name) {
//player decides from which inventory items should be sold.
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType type = MapleInventoryType.getByType(invTypeId);
Item it = getInventory(type).findByName(name);
@@ -7539,10 +7561,9 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public void increaseEquipExp(int expGain) {
if(expGain < 0) expGain = Integer.MAX_VALUE;
MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
for (Item item : getInventory(MapleInventoryType.EQUIPPED).list()) {
Equip nEquip = (Equip) item;
String itemName = mii.getName(nEquip.getItemId());
String itemName = ii.getName(nEquip.getItemId());
if (itemName == null) {
continue;
}
@@ -7554,12 +7575,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public void showAllEquipFeatures() {
MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
String showMsg = "";
for (Item item : getInventory(MapleInventoryType.EQUIPPED).list()) {
Equip nEquip = (Equip) item;
String itemName = mii.getName(nEquip.getItemId());
String itemName = ii.getName(nEquip.getItemId());
if (itemName == null) {
continue;
}

View File

@@ -68,8 +68,6 @@ import scripting.npc.NPCConversationManager;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.MapleMiniGame;
import server.MaplePlayerShop;
import server.life.MapleMonster;
import server.MapleTrade;
import server.TimerManager;
@@ -1298,7 +1296,7 @@ public class MapleClient {
MapleTrade.cancelTrade(getPlayer());
}
HiredMerchant merchant = player.getHiredMerchant();
MapleHiredMerchant merchant = player.getHiredMerchant();
if (merchant != null) {
if (merchant.isOwner(getPlayer())) {
merchant.setOpen(true);

View File

@@ -1413,7 +1413,31 @@ public class Commands {
MapleCharacter player = c.getPlayer();
MapleCharacter victim;
switch(sub[0]) {
switch(sub[0]) {
case "fly":
if (sub.length < 2) {
player.yellowMessage("Syntax: !fly <on/off>");
break;
}
Integer accid = c.getAccID();
String sendStr = "";
if(sub[1].equalsIgnoreCase("on")) {
sendStr += "GM Fly feature enabled. With fly active, GM's cannot attack.";
if(!srv.canFly(accid)) sendStr += " Re-login to take effect.";
srv.changeFly(c.getAccID(), true);
} else {
player.dropMessage(6, "GM Fly feature disabled. GM's can now attack.");
if(srv.canFly(accid)) sendStr += " Re-login to take effect.";
srv.changeFly(c.getAccID(), false);
}
player.dropMessage(6, sendStr);
break;
case "spawn":
if (sub.length < 2) {
player.yellowMessage("Syntax: !spawn <mobid>");
@@ -1592,12 +1616,12 @@ public class Commands {
Server.getInstance().broadcastGMMessage(MaplePacketCreator.serverNotice(5, message_));
break;
case "ignored":
case "ignored":
for (String ign : MapleLogger.ignored){
player.yellowMessage(ign + " is being ignored.");
}
break;
case "pos":
float xpos = player.getPosition().x;
float ypos = player.getPosition().y;
@@ -2552,6 +2576,14 @@ public class Commands {
player.dropMessage(5, "Quest Cache for quest " + sub[1] + " cleared.");
break;
case "fred":
c.announce(MaplePacketCreator.fredrickMessage(Byte.valueOf(sub[1])));
break;
case "owl":
c.announce(MaplePacketCreator.getOwlMessage(Integer.valueOf(sub[1])));
break;
default:
return false;
}

View File

@@ -128,6 +128,16 @@ public class MapleInventory implements Iterable<Item> {
return qty;
}
public int countCleanById(int itemId) {
int qty = 0;
for (Item item : list()) {
if (item.getItemId() == itemId && item.getOwner().equals("")) {
qty += item.getQuantity();
}
}
return qty;
}
public int freeSlotCountById(int itemId, int required) {
List<Item> itemList = listById(itemId);
int openSlot = 0;
@@ -297,6 +307,7 @@ public class MapleInventory implements Iterable<Item> {
public boolean isFull(int margin) {
lock.lock();
try {
//System.out.print("(" + inventory.size() + " " + margin + " <> " + slotLimit + ")");
return inventory.size() + margin >= slotLimit;
} finally {
lock.unlock();
@@ -306,6 +317,7 @@ public class MapleInventory implements Iterable<Item> {
public boolean isFullAfterSomeItems(int margin, int used) {
lock.lock();
try {
//System.out.print("(" + inventory.size() + " " + margin + " <> " + slotLimit + " -" + used + ")");
return inventory.size() + margin >= slotLimit - used;
} finally {
lock.unlock();
@@ -445,8 +457,12 @@ public class MapleInventory implements Iterable<Item> {
int usedSlots = typesSlotsUsed.get(itemType);
Long itemId = it.getKey() >> 32L;
//System.out.print("inserting " + itemId.intValue() + " with type " + itemType + " qty " + it.getValue() + " owner '" + rcvOwners.get(it.getKey()) + "' current usedSlots:");
//for(Integer i : typesSlotsUsed) System.out.print(" " + i);
int result = MapleInventoryManipulator.checkSpaceProgressively(c, itemId.intValue(), it.getValue(), rcvOwners.get(it.getKey()), usedSlots);
boolean hasSpace = ((result % 2) != 0);
//System.out.print(" -> hasSpace: " + hasSpace + " RESULT : " + result + "\n");
if(!hasSpace) return false;
typesSlotsUsed.set(itemType, (result >> 1));

View File

@@ -2,11 +2,17 @@ package constants;
import client.MapleJob;
import constants.skills.Aran;
import server.maps.MapleMap;
import server.maps.FieldLimit;
/*
* @author kevintjuh93
* @author Ronan
*/
public class GameConstants {
//public static final int[] OWL_DATA = {2000014, 2000015, 2000016};
public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804};
// Ronan's rates upgrade system
private static final int[] DROP_RATE_GAIN = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
private static final int[] MESO_RATE_GAIN = {1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105};
@@ -137,6 +143,23 @@ public class GameConstants {
return skill >= 9001000 && skill <= 9101008 || skill >= 8001000 && skill <= 8001001;
}
public static boolean isFreeMarketRoom(int mapid) {
return mapid > 910000000 && mapid < 910000023;
}
public static boolean isMerchantLocked(MapleMap map) {
if(FieldLimit.CANNOTMIGRATE.check(map.getFieldLimit())) { // maps that cannot access cash shop cannot access merchants too (except FM rooms).
return true;
}
switch(map.getId()) {
case 910000000:
return true;
}
return false;
}
public static boolean isBossRush(int mapid) {
return mapid >= 970030100 && mapid <= 970042711;
}

View File

@@ -43,13 +43,13 @@ public class ServerConstants {
public static final boolean USE_ITEM_SORT = true;
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
public static final boolean USE_PARTY_SEARCH = false;
public static final boolean USE_MERCHANT_ANYWHERE = true; //Enables player shops and hired merchants outside FM rooms (except FM entrance).
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
public static final boolean USE_SERVER_AUTOASSIGNER = true; //Server-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
public static final boolean USE_REFRESH_RANK_MOVE = true;
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster)
public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop.
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
public static final boolean USE_BUFF_MOST_SIGNIFICANT = true; //When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats.

View File

@@ -147,7 +147,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler());
registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler());
registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler());
registerHandler(RecvOpcode.USE_SUMMON_BAG, new UseSummonBag());
registerHandler(RecvOpcode.USE_SUMMON_BAG, new UseSummonBagHandler());
registerHandler(RecvOpcode.FACE_EXPRESSION, new FaceExpressionHandler());
registerHandler(RecvOpcode.HEAL_OVER_TIME, new HealOvertimeHandler());
registerHandler(RecvOpcode.ITEM_PICKUP, new ItemPickupHandler());
@@ -182,7 +182,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.BBS_OPERATION, new BBSOperationHandler());
registerHandler(RecvOpcode.SKILL_EFFECT, new SkillEffectHandler());
registerHandler(RecvOpcode.MESSENGER, new MessengerHandler());
registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimation());
registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimationHandler());
registerHandler(RecvOpcode.CHECK_CASH, new TouchingCashShopHandler());
registerHandler(RecvOpcode.CASHSHOP_OPERATION, new CashOperationHandler());
registerHandler(RecvOpcode.COUPON_CODE, new CouponCodeHandler());
@@ -205,6 +205,8 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.SPOUSE_CHAT, new SpouseChatHandler());
registerHandler(RecvOpcode.PET_AUTO_POT, new PetAutoPotHandler());
registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler());
registerHandler(RecvOpcode.OWL_ACTION, new UseOwlOfMinervaHandler());
registerHandler(RecvOpcode.OWL_WARP, new OwlWarpHandler());
registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler());
registerHandler(RecvOpcode.TROCK_ADD_MAP, new TrockAddMapHandler());
registerHandler(RecvOpcode.HIRED_MERCHANT_REQUEST, new HiredMerchantRequest());

View File

@@ -76,7 +76,9 @@ public enum RecvOpcode {
HIRED_MERCHANT_REQUEST(0x3F),
FREDRICK_ACTION(0x40),
DUEY_ACTION(0x41),
ADMIN_SHOP(0x44),//oh lol
OWL_ACTION(0x42), //sends most searched info to client
OWL_WARP(0x43), //handles player warp to store
ADMIN_SHOP(0x44),
ITEM_SORT(0x45),
ITEM_SORT2(0x46),
ITEM_MOVE(0x47),
@@ -87,7 +89,7 @@ public enum RecvOpcode {
USE_MOUNT_FOOD(0x4D),
SCRIPTED_ITEM(0x4E),
USE_CASH_ITEM(0x4F),
//USE_OWL_ITEM(0x50), ... no idea
USE_CATCH_ITEM(0x51),
USE_SKILL_BOOK(0x52),
USE_TELEPORT_ROCK(0x54),

View File

@@ -71,6 +71,7 @@ import java.util.Calendar;
import server.quest.MapleQuest;
public class Server implements Runnable {
private static final Set<Integer> activeFly = new HashSet<>();
private static final Map<Integer, Integer> couponRates = new LinkedHashMap<>();
private static final List<Integer> activeCoupons = new LinkedList<>();
@@ -699,6 +700,18 @@ public class Server implements Runnable {
return false;
}
public void changeFly(Integer accountid, boolean canFly) {
if(canFly) {
activeFly.add(accountid);
} else {
activeFly.remove(accountid);
}
}
public boolean canFly(Integer accountid) {
return activeFly.contains(accountid);
}
public World getWorld(int id) {
return worlds.get(id);
}

View File

@@ -57,7 +57,7 @@ import server.TimerManager;
import server.events.gm.MapleEvent;
import server.expeditions.MapleExpedition;
import server.expeditions.MapleExpeditionType;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import server.maps.MapleMap;
import server.maps.MapleMapFactory;
import tools.MaplePacketCreator;
@@ -73,7 +73,7 @@ public final class Channel {
private String ip, serverMessage;
private MapleMapFactory mapFactory;
private EventScriptManager eventSM;
private Map<Integer, HiredMerchant> hiredMerchants = new HashMap<>();
private Map<Integer, MapleHiredMerchant> hiredMerchants = new HashMap<>();
private final Map<Integer, Integer> storedVars = new HashMap<>();
private ReentrantReadWriteLock merchant_lock = new ReentrantReadWriteLock(true);
private ReadLock merchRlock = merchant_lock.readLock();
@@ -152,7 +152,7 @@ public final class Channel {
public void closeAllMerchants() {
merchWlock.lock();
try {
final Iterator<HiredMerchant> hmit = hiredMerchants.values().iterator();
final Iterator<MapleHiredMerchant> hmit = hiredMerchants.values().iterator();
while (hmit.hasNext()) {
hmit.next().forceClose();
hmit.remove();
@@ -246,7 +246,7 @@ public final class Channel {
}
}
public Map<Integer, HiredMerchant> getHiredMerchants() {
public Map<Integer, MapleHiredMerchant> getHiredMerchants() {
merchRlock.lock();
try {
return Collections.unmodifiableMap(hiredMerchants);
@@ -255,7 +255,7 @@ public final class Channel {
}
}
public void addHiredMerchant(int chrid, HiredMerchant hm) {
public void addHiredMerchant(int chrid, MapleHiredMerchant hm) {
merchWlock.lock();
try {
hiredMerchants.put(chrid, hm);

View File

@@ -75,7 +75,7 @@ public class FredrickHandler extends AbstractMaplePacketHandler {
Item item = items.get(i).getLeft();
MapleInventoryManipulator.addFromDrop(c, item, false);
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
FilePrinter.printError(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
FilePrinter.print(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
}
c.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E));
@@ -97,12 +97,7 @@ public class FredrickHandler extends AbstractMaplePacketHandler {
if (chr.getMeso() + chr.getMerchantMeso() < 0) {
return false;
}
if (!MapleInventory.checkSpots(chr, items)) {
return false;
}
return true;
return MapleInventory.checkSpotsAndOwnership(chr, items);
}
private static boolean deleteItems(MapleCharacter chr) {

View File

@@ -26,7 +26,7 @@ import client.MapleCharacter;
import java.sql.SQLException;
import java.util.Arrays;
import client.MapleClient;
import constants.ServerConstants;
import constants.GameConstants;
import net.AbstractMaplePacketHandler;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
@@ -39,7 +39,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
public final class HiredMerchantRequest extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && ((ServerConstants.USE_MERCHANT_ANYWHERE && chr.getMapId() != 910000000) || (chr.getMapId() > 910000000 && chr.getMapId() < 910000023))) {
if (chr.getMap().getMapObjectsInRange(chr.getPosition(), 23000, Arrays.asList(MapleMapObjectType.HIRED_MERCHANT)).isEmpty() && (GameConstants.isFreeMarketRoom(chr.getMapId()))) {
if (!chr.hasMerchant()) {
try {
if (ItemFactory.MERCHANT.loadItems(chr.getId(), false).isEmpty() && chr.getMerchantMeso() == 0) {

View File

@@ -27,7 +27,7 @@ import net.SendOpcode;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.data.output.MaplePacketLittleEndianWriter;
public final class NPCAnimation extends AbstractMaplePacketHandler {
public final class NPCAnimationHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
int length = (int) slea.available();

View File

@@ -0,0 +1,106 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 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 version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
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.channel.handlers;
import constants.GameConstants;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import server.MaplePlayerShop;
import server.maps.MapleHiredMerchant;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
/*
* @author Ronan
*/
public final class OwlWarpHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int ownerid = slea.readInt();
int mapid = slea.readInt();
MapleHiredMerchant hm = c.getWorldServer().getHiredMerchant(ownerid); // if both hired merchant and player shop is on the same map
MaplePlayerShop ps;
if(hm == null || hm.getMapId() != mapid || !hm.hasItem(c.getPlayer().getOwlSearch())) {
ps = c.getWorldServer().getPlayerShop(ownerid);
if(ps == null || ps.getMapId() != mapid || !ps.hasItem(c.getPlayer().getOwlSearch())) {
if(hm == null && ps == null) c.announce(MaplePacketCreator.getOwlMessage(1));
else c.announce(MaplePacketCreator.getOwlMessage(3));
return;
}
if(ps.isOpen()) {
if(GameConstants.isFreeMarketRoom(mapid)) {
if(ps.getChannel() == c.getChannel()) {
c.getPlayer().changeMap(mapid);
if(ps.isOpen()) { //change map has a delay, must double check
if(!ps.visitShop(c.getPlayer())) {
if(!ps.isBanned(c.getPlayer().getName())) c.announce(MaplePacketCreator.getOwlMessage(2));
else c.announce(MaplePacketCreator.getOwlMessage(17));
}
} else {
//c.announce(MaplePacketCreator.serverNotice(1, "That merchant has either been closed or is under maintenance."));
c.announce(MaplePacketCreator.getOwlMessage(18));
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "That shop is currently located in another channel. Current location: Channel " + hm.getChannel() + ", '" + hm.getMap().getMapName() + "'."));
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "That shop is currently located outside of the FM area. Current location: Channel " + hm.getChannel() + ", '" + hm.getMap().getMapName() + "'."));
}
} else {
//c.announce(MaplePacketCreator.serverNotice(1, "That merchant has either been closed or is under maintenance."));
c.announce(MaplePacketCreator.getOwlMessage(18));
}
} else {
if(hm.isOpen()) {
if(GameConstants.isFreeMarketRoom(mapid)) {
if(hm.getChannel() == c.getChannel()) {
c.getPlayer().changeMap(mapid);
if(hm.isOpen()) { //change map has a delay, must double check
if(hm.addVisitor(c.getPlayer())) {
c.announce(MaplePacketCreator.getHiredMerchant(c.getPlayer(), hm, false));
c.getPlayer().setHiredMerchant(hm);
} else {
//c.announce(MaplePacketCreator.serverNotice(1, hm.getOwner() + "'s merchant is full. Wait awhile before trying again."));
c.announce(MaplePacketCreator.getOwlMessage(2));
}
} else {
//c.announce(MaplePacketCreator.serverNotice(1, "That merchant has either been closed or is under maintenance."));
c.announce(MaplePacketCreator.getOwlMessage(18));
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "That merchant is currently located in another channel. Current location: Channel " + hm.getChannel() + ", '" + hm.getMap().getMapName() + "'."));
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "That merchant is currently located outside of the FM area. Current location: Channel " + hm.getChannel() + ", '" + hm.getMap().getMapName() + "'."));
}
} else {
//c.announce(MaplePacketCreator.serverNotice(1, "That merchant has either been closed or is under maintenance."));
c.announce(MaplePacketCreator.getOwlMessage(18));
}
}
}
}

View File

@@ -33,20 +33,19 @@ import constants.ServerConstants;
import java.util.Arrays;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleMiniGame;
import server.MaplePlayerShop;
import server.MaplePlayerShopItem;
import server.MapleTrade;
import constants.GameConstants;
import server.maps.FieldLimit;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
/**
@@ -114,6 +113,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
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
@@ -164,15 +164,16 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
return;
}
if (chr.getMapId() > 910000000 && chr.getMapId() < 910000023 || itemId > 5030000 && itemId < 5030012 || itemId > 5140000 && itemId < 5140006) {
if (GameConstants.isFreeMarketRoom(chr.getMapId()) || 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));
c.getWorldServer().registerPlayerShop(shop);
//c.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(1));
} else {
HiredMerchant merchant = new HiredMerchant(chr, itemId, desc);
MapleHiredMerchant merchant = new MapleHiredMerchant(chr, itemId, desc);
chr.setHiredMerchant(merchant);
c.getWorldServer().registerHiredMerchant(merchant);
chr.getClient().getChannelServer().addHiredMerchant(chr.getId(), merchant);
@@ -201,15 +202,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
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);
}
shop.visitShop(chr);
} else if (ob instanceof MapleMiniGame) {
MapleMiniGame game = (MapleMiniGame) ob;
if (game.hasFreeSlot() && !game.isVisitor(c.getPlayer())) {
@@ -226,14 +219,15 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
} else {
chr.getClient().announce(MaplePacketCreator.getMiniGameFull());
}
} else if (ob instanceof HiredMerchant && chr.getHiredMerchant() == null) {
HiredMerchant merchant = (HiredMerchant) ob;
} else if (ob instanceof MapleHiredMerchant && chr.getHiredMerchant() == null) {
MapleHiredMerchant merchant = (MapleHiredMerchant) ob;
if (merchant.isOwner(c.getPlayer())) {
merchant.setOpen(false);
merchant.removeAllVisitors("");
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.");
c.announce(MaplePacketCreator.hiredMerchantMaintenanceMessage());
return;
} else if (merchant.getFreeSlotThreadsafe() == -1) {
chr.dropMessage(1, "This shop has reached it's maximum capacity, please come by later.");
@@ -246,7 +240,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
}
} else if (mode == Action.CHAT.getCode()) { // chat lol
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (chr.getTrade() != null) {
chr.getTrade().chat(slea.readMapleAsciiString());
} else if (chr.getPlayerShop() != null) { //mini game
@@ -272,10 +266,18 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
} else if (mode == Action.OPEN.getCode()) {
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (shop != null && shop.isOwner(c.getPlayer())) {
slea.readByte();//01
if(ServerConstants.USE_ERASE_PERMIT_ON_OPENSHOP) {
try {
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5140000, 1, true, false);
} catch(RuntimeException re) {} // fella does not have a player shop permit...
}
chr.getMap().broadcastMessage(MaplePacketCreator.addCharBox(c.getPlayer(), 4));
shop.setOpen(true);
} else if (merchant != null && merchant.isOwner(c.getPlayer())) {
chr.setHasMerchant(true);
merchant.setOpen(true);
@@ -430,7 +432,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
sellItem.setQuantity(perBundle);
MaplePlayerShopItem item = new MaplePlayerShopItem(sellItem, bundles, price);
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (shop != null && shop.isOwner(c.getPlayer())) {
if (ivItem != null && ivItem.getQuantity() >= bundles * perBundle) {
shop.addItem(item);
@@ -476,7 +478,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.updateHiredMerchant(chr.getHiredMerchant(), chr));
}*/
} else if (mode == Action.MERCHANT_ORGANIZE.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (!merchant.isOwner(chr)) return;
if (chr.getMerchantMeso() > 0) {
@@ -513,19 +515,16 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
return;
}
MaplePlayerShop shop = chr.getPlayerShop();
HiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.getOwner().equals(chr.getName())) {
return;
}
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (shop != null && shop.isVisitor(c.getPlayer())) {
shop.buy(c, item, quantity);
shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
} else if (merchant != null) {
} else if (merchant != null && !merchant.isOwner(chr)) {
merchant.buy(c, item, quantity);
merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
}
} else if (mode == Action.TAKE_ITEM_BACK.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.isOwner(c.getPlayer())) {
int slot = slea.readShort();
MaplePlayerShopItem item = merchant.getItems().get(slot);
@@ -542,7 +541,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.updateHiredMerchant(merchant, c.getPlayer()));
}
} else if (mode == Action.CLOSE_MERCHANT.getCode()) {
HiredMerchant merchant = chr.getHiredMerchant();
MapleHiredMerchant merchant = chr.getHiredMerchant();
if (merchant != null && merchant.isOwner(c.getPlayer())) {
c.announce(MaplePacketCreator.hiredMerchantOwnerLeave());
c.announce(MaplePacketCreator.leaveHiredMerchant(0x00, 0x03));
@@ -550,18 +549,23 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
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.clearMessages();
merchant.setOpen(true);
MapleHiredMerchant merchant = chr.getHiredMerchant();
if(merchant != null) {
if (merchant.getItems().isEmpty() && merchant.isOwner(c.getPlayer())) {
merchant.closeShop(c, false);
chr.setHasMerchant(false);
}
if (merchant.isOwner(c.getPlayer())) {
merchant.clearMessages();
merchant.setOpen(true);
}
}
chr.setHiredMerchant(null);
c.announce(MaplePacketCreator.enableActions());
} else if (mode == Action.BAN_PLAYER.getCode()) {
slea.skip(1);
if (chr.getPlayerShop() != null && chr.getPlayerShop().isOwner(c.getPlayer())) {
chr.getPlayerShop().banPlayer(slea.readMapleAsciiString());
}

View File

@@ -278,7 +278,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
}
*/
if (player.isGM()){
Server.getInstance().broadcastGMMessage(MaplePacketCreator.earnTitleMessage("GM " + player.getName() + " has logged in"));
Server.getInstance().broadcastGMMessage(MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in"));
}
}

View File

@@ -25,9 +25,7 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import net.server.channel.Channel;
import net.server.Server;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
@@ -38,30 +36,27 @@ import tools.data.input.SeekableLittleEndianAccessor;
public class RemoteStoreHandler extends AbstractMaplePacketHandler {
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
HiredMerchant hm = getMerchant(c);
MapleHiredMerchant hm = getMerchant(c);
if (chr.hasMerchant() && hm != null) {
if (hm.getChannel() == chr.getClient().getChannel()) {
hm.setOpen(false);
hm.removeAllVisitors("");
hm.removeAllVisitors();
chr.setHiredMerchant(hm);
chr.announce(MaplePacketCreator.getHiredMerchant(chr, hm, false));
} else {
c.announce(MaplePacketCreator.remoteChannelChange((byte) (hm.getChannel() - 1)));
}
return;
} else {
chr.dropMessage(1, "You don't have a Merchant open");
chr.dropMessage(1, "You don't have a Merchant open.");
}
c.announce(MaplePacketCreator.enableActions());
}
public HiredMerchant getMerchant(MapleClient c) {
private MapleHiredMerchant getMerchant(MapleClient c) {
if (c.getPlayer().hasMerchant()) {
for (Channel cserv : Server.getInstance().getChannelsFromWorld(c.getWorld())) {
if (cserv.getHiredMerchants().get(c.getPlayer().getId()) != null) {
return cserv.getHiredMerchants().get(c.getPlayer().getId());
}
}
return c.getWorldServer().getHiredMerchant(c.getPlayer().getId());
}
return null;
}

View File

@@ -45,9 +45,11 @@ import net.server.Server;
import scripting.npc.NPCScriptManager;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MaplePlayerShopItem;
import server.MapleShop;
import server.MapleShopFactory;
import server.TimerManager;
import server.maps.AbstractMapleMapObject;
import server.maps.MapleMap;
import server.maps.MapleTVEffect;
import tools.MaplePacketCreator;
@@ -436,6 +438,16 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
player.gainMeso(ii.getMeso(itemId), true, false, true);
remove(c, itemId);
c.announce(MaplePacketCreator.enableActions());
} else if (itemType == 523) {
int itemid = slea.readInt();
player.setOwlSearch(itemid);
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = c.getWorldServer().getAvailableItemBundles(itemid);
if(!hmsAvailable.isEmpty()) remove(c, itemId);
c.announce(MaplePacketCreator.owlOfMinerva(c, itemid, hmsAvailable));
c.announce(MaplePacketCreator.enableActions());
} else if (itemType == 524) {
for (byte i = 0; i < 3; i++) {
MaplePet pet = player.getPet(i);

View File

@@ -0,0 +1,35 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 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 version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
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.channel.handlers;
import client.MapleClient;
import net.AbstractMaplePacketHandler;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.MaplePacketCreator;
public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
c.announce(MaplePacketCreator.getOwlOpen());
}
}

View File

@@ -36,7 +36,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
*
* @author AngelSL
*/
public final class UseSummonBag extends AbstractMaplePacketHandler {
public final class UseSummonBagHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {

View File

@@ -46,7 +46,7 @@ public class RankingWorker implements Runnable {
}
private void updateRanking(int job, int world) throws SQLException {
String sqlCharSelect = "SELECT c.id, " + (job != -1 ? "c.jobRank, c.jobRankMove" : "c.rank, c.rankMove") + ", a.lastlogin AS lastlogin, a.loggedin FROM characters AS c LEFT JOIN accounts AS a ON c.accountid = a.id WHERE c.world = ? ";
String sqlCharSelect = "SELECT c.id, " + (job != -1 ? "c.jobRank, c.jobRankMove" : "c.rank, c.rankMove") + ", a.lastlogin AS lastlogin, a.loggedin FROM characters AS c LEFT JOIN accounts AS a ON c.accountid = a.id WHERE c.gm < 2 AND c.world = ? ";
if (job != -1) {
sqlCharSelect += "AND c.job DIV 100 = ? ";
}

View File

@@ -34,6 +34,7 @@ import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
@@ -45,7 +46,8 @@ import java.util.HashSet;
import java.util.concurrent.ScheduledFuture;
import server.TimerManager;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import server.MaplePlayerShop;
import net.server.worker.CharacterAutosaverWorker;
import net.server.worker.MountTirednessWorker;
import net.server.worker.PetFullnessWorker;
@@ -56,8 +58,11 @@ import net.server.channel.CharacterIdChannelPair;
import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
import net.server.guild.MapleGuildSummary;
import server.MaplePlayerShopItem;
import server.maps.AbstractMapleMapObject;
import tools.DatabaseConnection;
import tools.MaplePacketCreator;
import tools.Pair;
/**
*
@@ -85,9 +90,11 @@ public class World {
private ScheduledFuture<?> mountsSchedule;
private long mountUpdate;
private Map<HiredMerchant, Byte> activeMerchants = new LinkedHashMap<>();
private Map<Integer, Pair<MapleHiredMerchant, Byte>> activeMerchants = new LinkedHashMap<>();
private long merchantUpdate;
private Map<Integer, MaplePlayerShop> activePlayerShops = new LinkedHashMap<>();
private ScheduledFuture<?> charactersSchedule;
public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int bossdroprate) {
@@ -778,47 +785,96 @@ public class World {
}
}
public void registerHiredMerchant(HiredMerchant hm) {
public void registerPlayerShop(MaplePlayerShop ps) {
synchronized(activePlayerShops) {
activePlayerShops.put(ps.getOwner().getId(), ps);
}
}
public void unregisterPlayerShop(MaplePlayerShop ps) {
synchronized(activePlayerShops) {
activePlayerShops.remove(ps.getOwner().getId());
}
}
public List<MaplePlayerShop> getActivePlayerShops() {
List<MaplePlayerShop> psList = new ArrayList<>();
synchronized(activePlayerShops) {
for(MaplePlayerShop mps : activePlayerShops.values()) {
psList.add(mps);
}
return psList;
}
}
public MaplePlayerShop getPlayerShop(int ownerid) {
synchronized(activePlayerShops) {
return activePlayerShops.get(ownerid);
}
}
public void registerHiredMerchant(MapleHiredMerchant hm) {
synchronized(activeMerchants) {
byte initProc;
if(System.currentTimeMillis() - merchantUpdate > 5 * 60 * 1000) initProc = 1;
else initProc = 0;
activeMerchants.put(hm, initProc);
activeMerchants.put(hm.getOwnerId(), new Pair<>(hm, initProc));
}
}
public void unregisterHiredMerchant(HiredMerchant hm) {
public void unregisterHiredMerchant(MapleHiredMerchant hm) {
synchronized(activeMerchants) {
activeMerchants.remove(hm);
activeMerchants.remove(hm.getOwnerId());
}
}
public void runHiredMerchantSchedule() {
Map<HiredMerchant, Byte> deployedMerchants;
Map<Integer, Pair<MapleHiredMerchant, Byte>> deployedMerchants;
synchronized(activeMerchants) {
merchantUpdate = System.currentTimeMillis();
deployedMerchants = Collections.unmodifiableMap(activeMerchants);
}
deployedMerchants = new LinkedHashMap<>(activeMerchants);
for(Map.Entry<HiredMerchant, Byte> dm: deployedMerchants.entrySet()) {
byte timeOn = dm.getValue();
if(timeOn <= 144) { // 1440 minutes == 24hrs
synchronized(activeMerchants) {
activeMerchants.put(dm.getKey(), (byte)(timeOn + 1));
}
} else {
HiredMerchant hm = dm.getKey();
hm.forceClose();
this.getChannel(hm.getChannel()).removeHiredMerchant(hm.getOwnerId());
for(Map.Entry<Integer, Pair<MapleHiredMerchant, Byte>> dm: deployedMerchants.entrySet()) {
byte timeOn = dm.getValue().getRight();
MapleHiredMerchant hm = dm.getValue().getLeft();
synchronized(activeMerchants) {
if(timeOn <= 144) { // 1440 minutes == 24hrs
activeMerchants.put(hm.getOwnerId(), new Pair<>(dm.getValue().getLeft(), (byte)(timeOn + 1)));
} else {
hm.forceClose();
this.getChannel(hm.getChannel()).removeHiredMerchant(hm.getOwnerId());
activeMerchants.remove(dm.getKey());
}
}
}
}
public List<MapleHiredMerchant> getActiveMerchants() {
List<MapleHiredMerchant> hmList = new ArrayList<>();
synchronized(activeMerchants) {
for(Pair<MapleHiredMerchant, Byte> hmp : activeMerchants.values()) {
MapleHiredMerchant hm = hmp.getLeft();
if(hm.isOpen()) {
hmList.add(hm);
}
}
return hmList;
}
}
public MapleHiredMerchant getHiredMerchant(int ownerid) {
synchronized(activeMerchants) {
if(activeMerchants.containsKey(ownerid)) {
return activeMerchants.get(ownerid).getLeft();
}
return null;
}
}
public void setServerMessage(String msg) {
for (Channel ch : channels) {
@@ -832,6 +888,36 @@ public class World {
}
}
public List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> getAvailableItemBundles(int itemid) {
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = new ArrayList<>();
for (MapleHiredMerchant hm : getActiveMerchants()) {
List<MaplePlayerShopItem> itemBundles = hm.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) hm));
}
}
for (MaplePlayerShop ps : getActivePlayerShops()) {
List<MaplePlayerShopItem> itemBundles = ps.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) ps));
}
}
Collections.sort(hmsAvailable, new Comparator<Pair<MaplePlayerShopItem, AbstractMapleMapObject>>() {
@Override
public int compare(Pair<MaplePlayerShopItem, AbstractMapleMapObject> p1, Pair<MaplePlayerShopItem, AbstractMapleMapObject> p2) {
return p1.getLeft().getPrice() - p2.getLeft().getPrice();
}
});
hmsAvailable.subList(0, Math.min(hmsAvailable.size(), 200)); //truncates the list to have up to 200 elements
return hmsAvailable;
}
public final void shutdown() {
for (Channel ch : getChannels()) {
ch.shutdown();

View File

@@ -267,6 +267,7 @@ public class MapleInventoryManipulator {
public static int checkSpaceProgressively(MapleClient c, int itemid, int quantity, String owner, int usedSlots) {
// return value --> bit0: if has space for this one;
// value after: new slots filled;
// assumption: equipments always have slotMax == 1.
int returnValue;
@@ -302,9 +303,11 @@ public class MapleInventoryManipulator {
returnValue = ((numSlotsNeeded + usedSlots) << 1);
returnValue += (numSlotsNeeded == 0 || !c.getPlayer().getInventory(type).isFullAfterSomeItems(numSlotsNeeded - 1, usedSlots)) ? 1 : 0;
//System.out.print(" needed " + numSlotsNeeded + " used " + usedSlots + " rval " + returnValue);
} else {
returnValue = ((1 + usedSlots) << 1);
returnValue = ((quantity + usedSlots) << 1);
returnValue += (!c.getPlayer().getInventory(type).isFullAfterSomeItems(0, usedSlots)) ? 1 : 0;
//System.out.print(" eqpneeded " + 1 + " used " + usedSlots + " rval " + returnValue);
}
return returnValue;

View File

@@ -916,20 +916,13 @@ public class MapleItemInformationProvider {
}
nEquip.setLevel((byte) (nEquip.getLevel() + 1));
}
}
if (ServerConstants.USE_PERFECT_SCROLLING == false && !ItemConstants.isCleanSlate(scrollId)) {
if (!assertGM && !usingWhiteScroll) {
} else {
if (ServerConstants.USE_PERFECT_SCROLLING == false && !usingWhiteScroll && !ItemConstants.isCleanSlate(scrollId) && !assertGM) {
nEquip.setUpgradeSlots((byte) (nEquip.getUpgradeSlots() - 1));
}
//nEquip.setLevel((byte) (nEquip.getLevel() + 1));
}
} else {
if (!usingWhiteScroll && !ItemConstants.isCleanSlate(scrollId) && !assertGM) {
nEquip.setUpgradeSlots((byte) (nEquip.getUpgradeSlots() - 1));
}
if (Randomizer.nextInt(101) < stats.get("cursed")) {
return null;
if (Randomizer.nextInt(101) < stats.get("cursed")) {
return null;
}
}
}
}

View File

@@ -26,25 +26,33 @@ import client.MapleClient;
import client.inventory.Item;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import net.SendOpcode;
import server.maps.AbstractMapleMapObject;
import server.maps.MapleMapObjectType;
import tools.MaplePacketCreator;
import tools.Pair;
import tools.data.output.MaplePacketLittleEndianWriter;
/**
*
* @author Matze
* @author Ronan (concurrency protection)
*/
public class MaplePlayerShop extends AbstractMapleMapObject {
private AtomicBoolean open = new AtomicBoolean(false);
private MapleCharacter owner;
private MapleCharacter[] visitors = new MapleCharacter[3];
private List<MaplePlayerShopItem> items = new ArrayList<>();
private MapleCharacter[] slot = {null, null, null};
private String description;
private int boughtnumber = 0;
private List<String> bannedList = new ArrayList<>();
private List<Pair<MapleCharacter, String>> chatLog = new LinkedList<>();
private Map<Integer, Byte> chatSlot = new LinkedHashMap<>();
public MaplePlayerShop(MapleCharacter owner, String description) {
this.setPosition(owner.getPosition());
@@ -52,96 +60,145 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
this.description = description;
}
public int getChannel() {
return owner.getClient().getChannel();
}
public int getMapId() {
return owner.getMapId();
}
public boolean isOpen() {
return open.get();
}
public void setOpen(boolean openShop) {
open.set(openShop);
}
public boolean hasFreeSlot() {
return visitors[0] == null || visitors[1] == null || visitors[2] == null;
synchronized (visitors) {
return visitors[0] == null || visitors[1] == null || visitors[2] == null;
}
}
public boolean isOwner(MapleCharacter c) {
return owner.equals(c);
}
public void addVisitor(MapleCharacter visitor) {
for (int i = 0; i < 3; i++) {
if (visitors[i] == null) {
visitors[i] = visitor;
if (this.getSlot(0) == null) {
this.setSlot(visitor, 0);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, 1));
} else if (this.getSlot(1) == null) {
this.setSlot(visitor, 1);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, 2));
} else if (this.getSlot(2) == null) {
this.setSlot(visitor, 2);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, 3));
visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(this.getOwner(), 1));
private void addVisitor(MapleCharacter visitor) {
synchronized (visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] == null) {
visitors[i] = visitor;
visitor.setSlot(i);
this.broadcast(MaplePacketCreator.getPlayerShopNewVisitor(visitor, i + 1));
if(i == 2) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(this.getOwner(), 1));
break;
}
break;
}
}
}
public void removeVisitor(MapleCharacter visitor) {
public void forceRemoveVisitor(MapleCharacter visitor) {
if (visitor == owner) {
owner.getMap().removeMapObject(this);
owner.setPlayerShop(null);
}
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
int slot_ = visitor.getSlot();
visitors[i] = null;
this.setSlot(null, i);
visitor.setSlot(-1);
this.broadcast(MaplePacketCreator.getPlayerShopRemoveVisitor(slot_ + 1));
this.broadcast(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
return;
}
}
}
public void removeVisitor(MapleCharacter visitor) {
if (visitor == owner) {
owner.getMap().removeMapObject(this);
owner.setPlayerShop(null);
} else {
synchronized (visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getId() == visitor.getId()) {
visitor.setSlot(-1); //absolutely cant remove player slot for late players without dc'ing them... heh
for(int j = i; j < 2; j++) {
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(j + 1));
visitors[j] = visitors[j + 1];
if(visitors[j] != null) visitors[j].setSlot(j);
}
visitors[2] = null;
for(int j = i; j < 2; j++) {
if(visitors[j] != null) owner.announce(MaplePacketCreator.getPlayerShopNewVisitor(visitors[j], j + 1));
}
this.broadcastRestoreToVisitors();
return;
}
}
}
if(this.getOwner().getPlayerShop() != null) visitor.getMap().broadcastMessage(MaplePacketCreator.addCharBox(this.getOwner(), 4));
}
}
public boolean isVisitor(MapleCharacter visitor) {
return visitors[0] == visitor || visitors[1] == visitor || visitors[2] == visitor;
synchronized (visitors) {
return visitors[0] == visitor || visitors[1] == visitor || visitors[2] == visitor;
}
}
public void addItem(MaplePlayerShopItem item) {
items.add(item);
synchronized (items) {
items.add(item);
}
}
public void removeItem(int item) {
items.remove(item);
synchronized (items) {
items.remove(item);
}
}
/**
* no warnings for now o.op
* no warnings for now o.o
* @param c
* @param item
* @param quantity
*/
public void buy(MapleClient c, int item, short quantity) {
if (isVisitor(c.getPlayer())) {
MaplePlayerShopItem pItem = items.get(item);
Item newItem = pItem.getItem().copy();
newItem.setQuantity(newItem.getQuantity());
if (quantity < 1 || pItem.getBundles() < 1 || newItem.getQuantity() > pItem.getBundles() || !pItem.isExist()) {
return;
} else if (newItem.getType() == 1 && newItem.getQuantity() > 1) {
return;
}
synchronized (c.getPlayer()) {
if (c.getPlayer().getMeso() >= (long) pItem.getPrice() * quantity) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, false)) {
c.getPlayer().gainMeso(-pItem.getPrice() * quantity, true);
owner.gainMeso(pItem.getPrice() * quantity, true);
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
if (++boughtnumber == items.size()) {
owner.setPlayerShop(null);
owner.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(owner));
this.removeVisitors();
owner.dropMessage(1, "Your items are sold out, and therefore your shop is closed.");
synchronized (items) {
if (isVisitor(c.getPlayer())) {
MaplePlayerShopItem pItem = items.get(item);
Item newItem = pItem.getItem().copy();
newItem.setQuantity(newItem.getQuantity());
if (quantity < 1 || pItem.getBundles() < 1 || newItem.getQuantity() > pItem.getBundles() || !pItem.isExist()) {
return;
} else if (newItem.getType() == 1 && newItem.getQuantity() > 1) {
return;
}
synchronized (c.getPlayer()) {
if (c.getPlayer().getMeso() >= (long) pItem.getPrice() * quantity) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, false)) {
c.getPlayer().gainMeso(-pItem.getPrice() * quantity, true);
owner.gainMeso(pItem.getPrice() * quantity, true);
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
if (++boughtnumber == items.size()) {
owner.setPlayerShop(null);
this.setOpen(false);
this.closeShop();
owner.dropMessage(1, "Your items are sold out, and therefore your shop is closed.");
}
}
} else {
c.getPlayer().dropMessage(1, "Your inventory is full. Please clean a slot before buying this item.");
}
} else {
c.getPlayer().dropMessage(1, "Your inventory is full. Please clean a slot before buying this item.");
}
}
}
@@ -149,26 +206,51 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
}
public void broadcastToVisitors(final byte[] packet) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(packet);
synchronized (visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(packet);
}
}
}
}
public void broadcastRestoreToVisitors() {
synchronized (visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShopRemoveVisitor(i + 1));
}
}
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.getPlayerShop(this, false));
}
}
recoverChatLog();
}
}
public void removeVisitors() {
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.shopErrorMessage(10, 1));
removeVisitor(visitors[i]);
List<MapleCharacter> visitorList = new ArrayList<>(3);
synchronized (visitors) {
try {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].getClient().announce(MaplePacketCreator.shopErrorMessage(10, 1));
visitorList.add(visitors[i]);
}
}
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
for(MapleCharacter mc : visitorList) forceRemoveVisitor(mc);
if (owner != null) {
removeVisitor(getOwner());
forceRemoveVisitor(getOwner());
}
}
@@ -188,23 +270,61 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
broadcastToVisitors(packet);
}
public void chat(MapleClient c, String chat) {
private byte getVisitorSlot(MapleCharacter chr) {
byte s = 0;
for (MapleCharacter mc : getVisitors()) {
s++;
if (mc != null) {
if (mc.getName().equalsIgnoreCase(c.getPlayer().getName())) {
if (mc.getName().equalsIgnoreCase(chr.getName())) {
break;
}
} else if (s == 3) {
s = 0;
}
}
return s;
}
public void chat(MapleClient c, String chat) {
byte s = getVisitorSlot(c.getPlayer());
synchronized(chatLog) {
chatLog.add(new Pair<>(c.getPlayer(), chat));
if(chatLog.size() > 25) chatLog.remove(0);
chatSlot.put(c.getPlayer().getId(), s);
}
broadcast(MaplePacketCreator.getPlayerShopChat(c.getPlayer(), chat, s));
}
private void recoverChatLog() {
synchronized(chatLog) {
for(Pair<MapleCharacter, String> it : chatLog) {
MapleCharacter chr = it.getLeft();
Byte pos = chatSlot.get(chr.getId());
broadcastToVisitors(MaplePacketCreator.getPlayerShopChat(chr, it.getRight(), pos));
}
}
}
private void clearChatLog() {
synchronized(chatLog) {
chatLog.clear();
}
}
public void closeShop() {
owner.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(owner));
clearChatLog();
removeVisitors();
}
public void sendShop(MapleClient c) {
c.announce(MaplePacketCreator.getPlayerShop(c, this, isOwner(c.getPlayer())));
synchronized(visitors) {
c.announce(MaplePacketCreator.getPlayerShop(this, isOwner(c.getPlayer())));
}
}
public MapleCharacter getOwner() {
@@ -212,22 +332,28 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
}
public MapleCharacter[] getVisitors() {
return visitors;
}
public MapleCharacter getSlot(int s) {
return slot[s];
}
private void setSlot(MapleCharacter person, int s) {
slot[s] = person;
if (person != null) {
person.setSlot(s);
synchronized(visitors) {
MapleCharacter[] copy = new MapleCharacter[3];
for(int i = 0; i < visitors.length; i++) copy[i] = visitors[i];
return copy;
}
}
public List<MaplePlayerShopItem> getItems() {
return Collections.unmodifiableList(items);
synchronized (items) {
return Collections.unmodifiableList(items);
}
}
public boolean hasItem(int itemid) {
for(MaplePlayerShopItem mpsi : getItems()) {
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
return true;
}
}
return false;
}
public String getDescription() {
@@ -242,18 +368,64 @@ public class MaplePlayerShop extends AbstractMapleMapObject {
if (!bannedList.contains(name)) {
bannedList.add(name);
}
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getName().equals(name)) {
visitors[i].getClient().announce(MaplePacketCreator.shopErrorMessage(5, 1));
removeVisitor(visitors[i]);
return; //I'm guessing this was the intended action
MapleCharacter target = null;
synchronized(visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null && visitors[i].getName().equals(name)) {
target = visitors[i];
break;
}
}
}
if(target != null) {
target.getClient().announce(MaplePacketCreator.shopErrorMessage(5, 1));
removeVisitor(target);
}
}
public boolean isBanned(String name) {
return bannedList.contains(name);
}
public synchronized boolean visitShop(MapleCharacter chr) {
if(!open.get()) {
chr.dropMessage(1, "This store is not yet open.");
return false;
}
if (this.isBanned(chr.getName())) {
chr.dropMessage(1, "You have been banned from this store.");
return false;
}
if (this.hasFreeSlot() && !this.isVisitor(chr)) {
this.addVisitor(chr);
chr.setPlayerShop(this);
this.sendShop(chr.getClient());
return true;
}
return false;
}
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
List<MaplePlayerShopItem> list = new LinkedList<>();
List<MaplePlayerShopItem> all = new ArrayList<>();
synchronized (items) {
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
}
for(MaplePlayerShopItem mpsi : all) {
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
list.add(mpsi);
}
}
return list;
}
@Override
public void sendDestroyData(MapleClient client) {

View File

@@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
@@ -46,8 +47,9 @@ import tools.Pair;
/**
*
* @author XoticStory
* @author Ronan (concurrency protection)
*/
public class HiredMerchant extends AbstractMapleMapObject {
public class MapleHiredMerchant extends AbstractMapleMapObject {
private int ownerId, itemId, mesos = 0;
private int channel, world;
@@ -58,10 +60,10 @@ public class HiredMerchant extends AbstractMapleMapObject {
private final List<MaplePlayerShopItem> items = new LinkedList<>();
private List<Pair<String, Byte>> messages = new LinkedList<>();
private List<SoldItem> sold = new LinkedList<>();
private boolean open;
private AtomicBoolean open = new AtomicBoolean();
private MapleMap map;
public HiredMerchant(final MapleCharacter owner, int itemId, String desc) {
public MapleHiredMerchant(final MapleCharacter owner, int itemId, String desc) {
this.setPosition(owner.getPosition());
this.start = System.currentTimeMillis();
this.ownerId = owner.getId();
@@ -87,13 +89,17 @@ public class HiredMerchant extends AbstractMapleMapObject {
}
}
public void addVisitor(MapleCharacter visitor) {
public boolean addVisitor(MapleCharacter visitor) {
synchronized(visitors) {
int i = this.getFreeSlot();
if (i > -1) {
visitors[i] = visitor;
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
return true;
}
return false;
}
}
@@ -105,9 +111,7 @@ public class HiredMerchant extends AbstractMapleMapObject {
}
if (visitors[slot] != null && visitors[slot].getId() == visitor.getId()) {
visitors[slot] = null;
if (slot != -1) {
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
}
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
}
}
}
@@ -127,15 +131,15 @@ public class HiredMerchant extends AbstractMapleMapObject {
return -1; //Actually 0 because of the +1's.
}
public void removeAllVisitors(String message) {
public void removeAllVisitors() {
synchronized(visitors) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].setHiredMerchant(null);
visitors[i].getClient().announce(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
if (message.length() > 0) {
visitors[i].dropMessage(1, message);
}
visitors[i].getClient().announce(MaplePacketCreator.hiredMerchantMaintenanceMessage());
visitors[i] = null;
}
}
@@ -163,7 +167,7 @@ public class HiredMerchant extends AbstractMapleMapObject {
}
int price = (int)Math.min((long)pItem.getPrice() * quantity, Integer.MAX_VALUE);
if (c.getPlayer().getMeso() >= price) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, true)) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, false)) {
c.getPlayer().gainMeso(-price, false);
synchronized (sold) {
@@ -322,6 +326,16 @@ public class HiredMerchant extends AbstractMapleMapObject {
return Collections.unmodifiableList(items);
}
}
public boolean hasItem(int itemid) {
for(MaplePlayerShopItem mpsi : getItems()) {
if(mpsi.getItem().getItemId() == itemid && mpsi.isExist() && mpsi.getBundles() > 0) {
return true;
}
}
return false;
}
public void addItem(MaplePlayerShopItem item) {
synchronized (items) {
@@ -367,11 +381,11 @@ public class HiredMerchant extends AbstractMapleMapObject {
}
public boolean isOpen() {
return open;
return open.get();
}
public void setOpen(boolean set) {
this.open = set;
open.getAndSet(set);
}
public int getItemId() {
@@ -389,7 +403,25 @@ public class HiredMerchant extends AbstractMapleMapObject {
synchronized (messages) {
messages.add(new Pair<>(message, slot));
}
broadcastToVisitors(MaplePacketCreator.hiredMerchantChat(message, slot));
broadcastToVisitorsThreadsafe(MaplePacketCreator.hiredMerchantChat(message, slot));
}
public List<MaplePlayerShopItem> sendAvailableBundles(int itemid) {
List<MaplePlayerShopItem> list = new LinkedList<>();
List<MaplePlayerShopItem> all = new ArrayList<>();
if(!open.get()) return list;
synchronized (items) {
for(MaplePlayerShopItem mpsi : items) all.add(mpsi);
}
for(MaplePlayerShopItem mpsi : all) {
if(mpsi.getItem().getItemId() == itemid && mpsi.getBundles() > 0 && mpsi.isExist()) {
list.add(mpsi);
}
}
return list;
}
public void saveItems(boolean shutdown) throws SQLException {
@@ -490,6 +522,10 @@ public class HiredMerchant extends AbstractMapleMapObject {
public int getMapId() {
return map.getId();
}
public MapleMap getMap() {
return map;
}
public List<SoldItem> getSold() {
synchronized (sold) {

View File

@@ -62,7 +62,7 @@ import server.events.gm.MapleSnowball;
import server.life.MapleMonster;
import server.life.MapleNPC;
import server.life.MobSkill;
import server.maps.HiredMerchant;
import server.maps.MapleHiredMerchant;
import server.maps.MapleDoor;
import server.maps.MapleDoorObject;
import server.maps.MapleDragon;
@@ -106,6 +106,7 @@ import constants.ServerConstants;
import constants.skills.Buccaneer;
import constants.skills.Corsair;
import constants.skills.ThunderBreaker;
import server.maps.AbstractMapleMapObject;
/**
*
@@ -686,7 +687,7 @@ public class MaplePacketCreator {
mplew.writeBool(c.getGMLevel() > 0);
mplew.write(0); // Admin Byte. 0x80,0x40,0x20.. Rubbish.
mplew.write((c.getGMLevel() > 0 && Server.getInstance().canFly(c.getAccID())) ? 0x80 : 0); // Admin Byte. 0x80,0x40,0x20.. Rubbish.
mplew.write(0); // Country Code.
mplew.writeMapleAsciiString(c.getAccountName());
@@ -1856,11 +1857,13 @@ public class MaplePacketCreator {
mplew.writeInt(chr.getMount().getExp());
mplew.writeInt(chr.getMount().getTiredness());
}
if (chr.getPlayerShop() != null && chr.getPlayerShop().isOwner(chr)) {
if (chr.getPlayerShop().hasFreeSlot()) {
addAnnounceBox(mplew, chr.getPlayerShop(), chr.getPlayerShop().getVisitors().length);
MaplePlayerShop mps = chr.getPlayerShop();
if (mps != null && mps.isOwner(chr)) {
if (mps.hasFreeSlot()) {
addAnnounceBox(mplew, mps, mps.getVisitors().length);
} else {
addAnnounceBox(mplew, chr.getPlayerShop(), 1);
addAnnounceBox(mplew, mps, 1);
}
} else if (chr.getMiniGame() != null && chr.getMiniGame().isOwner(chr)) {
if (chr.getMiniGame().hasFreeSlot()) {
@@ -2728,8 +2731,8 @@ public class MaplePacketCreator {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(4);
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.EXIT.getCode());
if (slot > 0) {
mplew.write(slot);
if (slot != 0) {
mplew.writeShort(slot);
}
return mplew.getPacket();
}
@@ -2794,7 +2797,7 @@ public class MaplePacketCreator {
* @param owner
* @return
*/
public static byte[] getPlayerShop(MapleClient c, MaplePlayerShop shop, boolean owner) {
public static byte[] getPlayerShop(MaplePlayerShop shop, boolean owner) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.ROOM.getCode());
@@ -2804,9 +2807,16 @@ public class MaplePacketCreator {
mplew.write(0);
addCharLook(mplew, shop.getOwner(), false);
mplew.writeMapleAsciiString(shop.getOwner().getName());
mplew.write(1);
addCharLook(mplew, shop.getOwner(), false);
mplew.writeMapleAsciiString(shop.getOwner().getName());
MapleCharacter visitors[] = shop.getVisitors();
for(int i = 0; i < 3; i++) {
if(visitors[i] != null) {
mplew.write(i + 1);
addCharLook(mplew, visitors[i], false);
mplew.writeMapleAsciiString(visitors[i].getName());
}
}
mplew.write(0xFF);
mplew.writeMapleAsciiString(shop.getDescription());
List<MaplePlayerShopItem> items = shop.getItems();
@@ -4943,38 +4953,84 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] owlOfMinerva(MapleClient c, int itemid, List<HiredMerchant> hms, List<MaplePlayerShopItem> items) { //Thanks moongra, you save me some time :)
// 0: Success
// 1: The room is already closed.
// 2: You can't enter the room due to full capacity.
// 3: Other requests are being fulfilled this minute.
// 4: You can't do it while you're dead.
// 7: You are not allowed to trade other items at this point.
// 17: You may not enter this store.
// 18: The owner of the store is currently undergoing store maintenance. Please try again in a bit.
// 23: This can only be used inside the Free Market.
// default: This character is unable to do it.
public static byte[] getOwlMessage(int msg) {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(3);
mplew.writeShort(SendOpcode.SHOP_LINK_RESULT.getValue());
mplew.write(msg); // depending on the byte sent, a different message is sent.
return mplew.getPacket();
}
public static byte[] owlOfMinerva(MapleClient c, int itemid, List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable) {
byte itemType = MapleItemInformationProvider.getInstance().getInventoryType(itemid).getType();
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SHOP_SCANNER_RESULT.getValue()); // header.
mplew.write(6);
mplew.writeInt(0);
mplew.writeInt(itemid);
mplew.writeInt(hms.size());
for (HiredMerchant hm : hms) {
for (MaplePlayerShopItem item : items) {
mplew.writeMapleAsciiString(hm.getOwner());
mplew.writeInt(hm.getMapId());
mplew.writeMapleAsciiString(hm.getDescription());
mplew.writeInt(item.getItem().getQuantity());
mplew.writeInt(item.getBundles());
mplew.writeInt(item.getPrice());
mplew.writeInt(hm.getOwnerId());
mplew.write(hm.getFreeSlotThreadsafe() == -1 ? 1 : 0);
MapleCharacter chr = c.getChannelServer().getPlayerStorage().getCharacterById(hm.getOwnerId());
if ((chr != null) && (c.getChannel() == hm.getChannel())) {
mplew.write(1);
} else {
mplew.write(2);
}
if (item.getItem().getItemId() / 1000000 == 1) {
addItemInfo(mplew, item.getItem(), true);
}
mplew.writeInt(hmsAvailable.size());
for (Pair<MaplePlayerShopItem, AbstractMapleMapObject> hme : hmsAvailable) {
MaplePlayerShopItem item = hme.getLeft();
AbstractMapleMapObject mo = hme.getRight();
if(mo instanceof MaplePlayerShop) {
MaplePlayerShop ps = (MaplePlayerShop) mo;
MapleCharacter owner = ps.getOwner();
mplew.writeMapleAsciiString(owner.getName());
mplew.writeInt(owner.getMapId());
mplew.writeMapleAsciiString(ps.getDescription());
mplew.writeInt(item.getBundles());
mplew.writeInt(item.getItem().getQuantity());
mplew.writeInt(item.getPrice());
mplew.writeInt(owner.getId());
mplew.write(owner.getClient().getChannel() - 1);
} else {
MapleHiredMerchant hm = (MapleHiredMerchant) mo;
mplew.writeMapleAsciiString(hm.getOwner());
mplew.writeInt(hm.getMapId());
mplew.writeMapleAsciiString(hm.getDescription());
mplew.writeInt(item.getBundles());
mplew.writeInt(item.getItem().getQuantity());
mplew.writeInt(item.getPrice());
mplew.writeInt(hm.getOwnerId());
mplew.write(hm.getChannel() - 1);
}
mplew.write(itemType);
if (itemType == MapleInventoryType.EQUIP.getType()) {
addItemInfo(mplew, item.getItem(), true);
}
}
return mplew.getPacket();
}
public static byte[] getOwlOpen() {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SHOP_SCANNER_RESULT.getValue());
mplew.write(7);
mplew.write(GameConstants.OWL_DATA.length);
for (int i : GameConstants.OWL_DATA) {
mplew.writeInt(i);
}
return mplew.getPacket();
}
public static byte[] retrieveFirstMessage() {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.ENTRUSTED_SHOP_CHECK_RESULT.getValue()); // header.
@@ -4998,7 +5054,7 @@ public class MaplePacketCreator {
* 0x12 = FKING POPUP LOL
*/
public static byte[] getHiredMerchant(MapleCharacter chr, HiredMerchant hm, boolean firstTime) {//Thanks Dustin
public static byte[] getHiredMerchant(MapleCharacter chr, MapleHiredMerchant hm, boolean firstTime) {//Thanks Dustin
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.ROOM.getCode());
@@ -5007,11 +5063,13 @@ public class MaplePacketCreator {
mplew.writeShort(hm.getVisitorSlotThreadsafe(chr) + 1);
mplew.writeInt(hm.getItemId());
mplew.writeMapleAsciiString("Hired Merchant");
MapleCharacter visitors[] = hm.getVisitors();
for (int i = 0; i < 3; i++) {
if (hm.getVisitors()[i] != null) {
if (visitors[i] != null) {
mplew.write(i + 1);
addCharLook(mplew, hm.getVisitors()[i], false);
mplew.writeMapleAsciiString(hm.getVisitors()[i].getName());
addCharLook(mplew, visitors[i], false);
mplew.writeMapleAsciiString(visitors[i].getName());
}
}
mplew.write(-1);
@@ -5057,7 +5115,7 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] updateHiredMerchant(HiredMerchant hm, MapleCharacter chr) {
public static byte[] updateHiredMerchant(MapleHiredMerchant hm, MapleCharacter chr) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.UPDATE_MERCHANT.getCode());
@@ -5100,6 +5158,15 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] hiredMerchantMaintenanceMessage() {
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(5);
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.ROOM.getCode());
mplew.write(0x00);
mplew.write(0x12);
return mplew.getPacket();
}
public static byte[] leaveHiredMerchant(int slot, int status2) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
@@ -5119,7 +5186,7 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
public static byte[] spawnHiredMerchant(HiredMerchant hm) {
public static byte[] spawnHiredMerchant(MapleHiredMerchant hm) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.SPAWN_HIRED_MERCHANT.getValue());
mplew.writeInt(hm.getOwnerId());