Item pickup patch + Rescaled buyback fee + Tameness gain on Quests
Fixed a concurrency issue with registering picked items into the inventory, that would render some of the collected items to not be registered. Adjusted buyback fee, now using a value sensitive to the player level (starts scaling from level 30, caps at 120). Edited MaxHP of the boss Shade, now properly scaled to it's level. Fixed return scroll and summon bags being usable inside BRPQ rest points. Added missing etc drop data for the Extras/Leaders mobs from Zipangu. Implemented pet tameness gain on quests. Fixed a concurrency issue with the pet spawn handler, allowing possible PE exploits. Adjusted drop rates of omok pieces and match cards, now somewhat GMS-like. Shadow Meso now properly dispels mobs wdef/mdef up, as the skill description suggests. Fixed Shadow Meso using up projectiles (it should use only mesos).
This commit is contained in:
@@ -5230,6 +5230,14 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
return lastBuyback + ServerConstants.BUYBACK_COOLDOWN_MINUTES * 60 * 1000;
|
||||
}
|
||||
|
||||
private int getBuybackFee() {
|
||||
float fee = ServerConstants.BUYBACK_FEE;
|
||||
int grade = Math.min(Math.max(level, 30), 120) - 30;
|
||||
|
||||
fee += (grade * ServerConstants.BUYBACK_LEVEL_STACK_FEE);
|
||||
return (int) Math.floor(fee);
|
||||
}
|
||||
|
||||
public boolean couldBuyback() { // Ronan's buyback system
|
||||
long timeNow = System.currentTimeMillis();
|
||||
|
||||
@@ -5249,7 +5257,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
boolean usingMesos = ServerConstants.USE_BUYBACK_WITH_MESOS;
|
||||
int fee = ServerConstants.BUYBACK_FEE;
|
||||
int fee = getBuybackFee();
|
||||
if(usingMesos) fee *= ServerConstants.BUYBACK_MESO_MULTIPLIER;
|
||||
|
||||
if(!canBuyback(fee, usingMesos)) {
|
||||
@@ -5867,7 +5875,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
for (Pair<Item, MapleInventoryType> item : ItemFactory.INVENTORY.loadItems(ret.id, !channelserver)) {
|
||||
sandboxCheck |= item.getLeft().getFlag();
|
||||
|
||||
ret.getInventory(item.getRight()).addFromDB(item.getLeft());
|
||||
ret.getInventory(item.getRight()).addItemFromDB(item.getLeft());
|
||||
Item itemz = item.getLeft();
|
||||
if (itemz.getPetId() > -1) {
|
||||
MaplePet pet = itemz.getPet();
|
||||
@@ -7977,12 +7985,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
public void unequipPet(MaplePet pet, boolean shift_left, boolean hunger) {
|
||||
if (this.getPet(this.getPetIndex(pet)) != null) {
|
||||
this.getPet(this.getPetIndex(pet)).setSummoned(false);
|
||||
this.getPet(this.getPetIndex(pet)).saveToDb();
|
||||
byte petIdx = this.getPetIndex(pet);
|
||||
MaplePet chrPet = this.getPet(petIdx);
|
||||
|
||||
if (chrPet != null) {
|
||||
chrPet.setSummoned(false);
|
||||
chrPet.saveToDb();
|
||||
}
|
||||
|
||||
this.getClient().getWorldServer().unregisterPetHunger(this, getPetIndex(pet));
|
||||
this.getClient().getWorldServer().unregisterPetHunger(this, petIdx);
|
||||
getMap().broadcastMessage(this, MaplePacketCreator.showPet(this, pet, true, hunger), true);
|
||||
|
||||
removePet(pet, shift_left);
|
||||
|
||||
@@ -948,7 +948,7 @@ public class MapleClient {
|
||||
}
|
||||
}
|
||||
|
||||
private void clear() { //usable when defining client = null shortly after
|
||||
private void clear() {
|
||||
// player hard reference removal thanks to Steve (kaito1410)
|
||||
if (this.player != null) {
|
||||
this.player.empty(true); // clears schedules and stuff
|
||||
|
||||
@@ -60,25 +60,25 @@ public abstract class CharacterFactory {
|
||||
if(top > 0) {
|
||||
Item eq_top = ii.getEquipById(top);
|
||||
eq_top.setPosition((byte) -5);
|
||||
equipped.addFromDB(eq_top);
|
||||
equipped.addItemFromDB(eq_top);
|
||||
}
|
||||
|
||||
if(bottom > 0) {
|
||||
Item eq_bottom = ii.getEquipById(bottom);
|
||||
eq_bottom.setPosition((byte) -6);
|
||||
equipped.addFromDB(eq_bottom);
|
||||
equipped.addItemFromDB(eq_bottom);
|
||||
}
|
||||
|
||||
if(shoes > 0) {
|
||||
Item eq_shoes = ii.getEquipById(shoes);
|
||||
eq_shoes.setPosition((byte) -7);
|
||||
equipped.addFromDB(eq_shoes);
|
||||
equipped.addItemFromDB(eq_shoes);
|
||||
}
|
||||
|
||||
if(weapon > 0) {
|
||||
Item eq_weapon = ii.getEquipById(weapon);
|
||||
eq_weapon.setPosition((byte) -11);
|
||||
equipped.addFromDB(eq_weapon.copy());
|
||||
equipped.addItemFromDB(eq_weapon.copy());
|
||||
}
|
||||
|
||||
if (!newchar.insertNewChar(recipe)) {
|
||||
|
||||
@@ -214,20 +214,19 @@ public class MapleInventory implements Iterable<Item> {
|
||||
}
|
||||
|
||||
public short addItem(Item item) {
|
||||
short slotId = getNextFreeSlot();
|
||||
if (slotId < 0 || item == null) {
|
||||
short slotId = addSlot(item);
|
||||
if (slotId == -1) {
|
||||
return -1;
|
||||
}
|
||||
addSlot(slotId, item);
|
||||
item.setPosition(slotId);
|
||||
return slotId;
|
||||
}
|
||||
|
||||
public void addFromDB(Item item) {
|
||||
public void addItemFromDB(Item item) {
|
||||
if (item.getPosition() < 0 && !type.equals(MapleInventoryType.EQUIPPED)) {
|
||||
return;
|
||||
}
|
||||
addSlot(item.getPosition(), item);
|
||||
addSlotFromDB(item.getPosition(), item);
|
||||
}
|
||||
|
||||
private static boolean isSameOwner(Item source, Item target) {
|
||||
@@ -302,16 +301,39 @@ public class MapleInventory implements Iterable<Item> {
|
||||
}
|
||||
}
|
||||
|
||||
public void addSlot(short slot, Item item) {
|
||||
private short addSlot(Item item) {
|
||||
if(item == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.put(slot, item);
|
||||
short slotId = getNextFreeSlot();
|
||||
if (slotId < 0) {
|
||||
return -1;
|
||||
}
|
||||
inventory.put(slotId, item);
|
||||
|
||||
if(ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
|
||||
return slotId;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if(ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
|
||||
private void addSlotFromDB(short slot, Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.put(slot, item);
|
||||
|
||||
if(ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -498,10 +498,10 @@ public class MapleInventoryManipulator {
|
||||
}
|
||||
|
||||
source.setPosition(dst);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addFromDB(source);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addItemFromDB(source);
|
||||
if (target != null) {
|
||||
target.setPosition(src);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIP).addFromDB(target);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIP).addItemFromDB(target);
|
||||
}
|
||||
if (c.getPlayer().getBuffedValue(MapleBuffStat.BOOSTER) != null && ItemConstants.isWeapon(source.getItemId())) {
|
||||
c.getPlayer().cancelBuffStats(MapleBuffStat.BOOSTER);
|
||||
@@ -536,10 +536,10 @@ public class MapleInventoryManipulator {
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIP).removeSlot(dst);
|
||||
}
|
||||
source.setPosition(dst);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIP).addFromDB(source);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIP).addItemFromDB(source);
|
||||
if (target != null) {
|
||||
target.setPosition(src);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addFromDB(target);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addItemFromDB(target);
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(2, source, src))));
|
||||
c.getPlayer().equipChanged();
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
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 client.processor;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import client.SkillFactory;
|
||||
import provider.MapleDataTool;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import java.io.File;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana (just added locking on OdinMS' SpawnPetHandler method body)
|
||||
*/
|
||||
public class SpawnPetProcessor {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
|
||||
public static void processSpawnPet(MapleClient c, byte slot, boolean lead) {
|
||||
c.lockClient();
|
||||
try {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MaplePet pet = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getPet();
|
||||
if (pet == null) return;
|
||||
|
||||
int petid = pet.getItemId();
|
||||
if (petid == 5000028 || petid == 5000047) //Handles Dragon AND Robos
|
||||
{
|
||||
if (chr.haveItem(petid + 1)) {
|
||||
chr.dropMessage(5, "You can't hatch your " + (petid == 5000028 ? "Dragon egg" : "Robo egg") + " if you already have a Baby " + (petid == 5000028 ? "Dragon." : "Robo."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else {
|
||||
int evolveid = MapleDataTool.getInt("info/evol1", dataRoot.getData("Pet/" + petid + ".img"));
|
||||
int petId = MaplePet.createPet(evolveid);
|
||||
if (petId == -1) {
|
||||
return;
|
||||
}
|
||||
long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
|
||||
MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
|
||||
pet.deleteFromDb();
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (chr.getPetIndex(pet) != -1) {
|
||||
chr.unequipPet(pet, true);
|
||||
} else {
|
||||
if (chr.getSkillLevel(SkillFactory.getSkill(8)) == 0 && chr.getPet(0) != null) {
|
||||
chr.unequipPet(chr.getPet(0), false);
|
||||
}
|
||||
if (lead) {
|
||||
chr.shiftPetsRight();
|
||||
}
|
||||
Point pos = chr.getPosition();
|
||||
pos.y -= 12;
|
||||
pet.setPos(pos);
|
||||
pet.setFh(chr.getMap().getFootholds().findBelow(pet.getPos()).getId());
|
||||
pet.setStance(0);
|
||||
pet.setSummoned(true);
|
||||
pet.saveToDb();
|
||||
chr.addPet(pet);
|
||||
chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
|
||||
c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
chr.commitExcludedItems();
|
||||
chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
|
||||
}
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,7 +105,7 @@ public class ServerConstants {
|
||||
public static String TIMEZONE = "-GMT3";
|
||||
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
|
||||
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 5; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
|
||||
public static final byte MIN_UNDERLEVEL_TO_EXP_GAIN = 20; //Characters are unable to get EXP from a mob if their level are under this threshold, only if "USE_ENFORCE_MOB_LEVEL_RANGE" is enabled. For bosses, this attribute is doubled.
|
||||
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
|
||||
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
|
||||
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
|
||||
@@ -206,7 +206,8 @@ public class ServerConstants {
|
||||
|
||||
//Buyback Configuration
|
||||
public static final boolean USE_BUYBACK_WITH_MESOS = true; //Enables usage of either mesos or NX for the buyback fee.
|
||||
public static final int BUYBACK_FEE = 7770; //Sets the amount needed to buyback.
|
||||
public static final float BUYBACK_FEE = 77.70f; //Sets the base amount needed to buyback (level 30 or under will use the base value).
|
||||
public static final float BUYBACK_LEVEL_STACK_FEE = 85.47f; //Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak).
|
||||
public static final int BUYBACK_MESO_MULTIPLIER = 1000; //Sets a multiplier for the fee when using meso as the charge unit.
|
||||
public static final int BUYBACK_RETURN_MINUTES = 1; //Sets the maximum amount of time the player can wait before decide to buyback.
|
||||
public static final int BUYBACK_COOLDOWN_MINUTES = 7; //Sets the time the player must wait before using buyback again.
|
||||
|
||||
@@ -573,6 +573,7 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
} else {
|
||||
ret.charge = 0;
|
||||
}
|
||||
|
||||
lea.skip(8);
|
||||
ret.display = lea.readByte();
|
||||
ret.direction = lea.readByte();
|
||||
@@ -674,10 +675,10 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
calcDmgMax = (int) Math.round((chr.getTotalInt() * 4.8 + chr.getTotalLuk() * 4) * chr.getTotalMagic() / 1000);
|
||||
calcDmgMax = calcDmgMax * effect.getHp() / 100;
|
||||
}
|
||||
} else if(ret.skill == Hermit.SHADOW_MESO) {
|
||||
// Shadow Meso also has its own formula
|
||||
calcDmgMax = effect.getMoneyCon() * 10;
|
||||
calcDmgMax = (int) Math.floor(calcDmgMax * 1.5);
|
||||
} else if(ret.skill == Hermit.SHADOW_MESO) {
|
||||
// Shadow Meso also has its own formula
|
||||
calcDmgMax = effect.getMoneyCon() * 10;
|
||||
calcDmgMax = (int) Math.floor(calcDmgMax * 1.5);
|
||||
} else {
|
||||
// Normal damage formula for skills
|
||||
calcDmgMax = calcDmgMax * effect.getDamage() / 100;
|
||||
@@ -812,6 +813,10 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl
|
||||
if(monster != null) {
|
||||
calcDmgMax = monster.getHp() / (50 - chr.getSkillLevel(skill));
|
||||
}
|
||||
} else if(ret.skill == Hermit.SHADOW_MESO) {
|
||||
if(monster != null) {
|
||||
monster.debuffMob(Hermit.SHADOW_MESO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
@@ -39,9 +40,11 @@ public final class MobDamageMobHandler extends AbstractMaplePacketHandler {
|
||||
int to = slea.readInt();
|
||||
slea.readByte();
|
||||
int dmg = slea.readInt();
|
||||
MapleMap map = c.getPlayer().getMap();
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
if (map.getMonsterByOid(from) != null && map.getMonsterByOid(to) != null) {
|
||||
map.damageMonster(c.getPlayer(), map.getMonsterByOid(to), dmg);
|
||||
map.damageMonster(chr, map.getMonsterByOid(to), dmg);
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(to, dmg));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import client.MapleBuffStat;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleJob;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Item;
|
||||
@@ -113,6 +112,20 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
if (effect.getCooldown() > 0) {
|
||||
c.announce(MaplePacketCreator.skillCooldown(attack.skill, effect.getCooldown()));
|
||||
}
|
||||
|
||||
if(attack.skill == 4111004) { // shadow meso
|
||||
bulletCount = 0;
|
||||
|
||||
int money = effect.getMoneyCon();
|
||||
if (money != 0) {
|
||||
int moneyMod = money / 2;
|
||||
money += Randomizer.nextInt(moneyMod);
|
||||
if (money > chr.getMeso()) {
|
||||
money = chr.getMeso();
|
||||
}
|
||||
chr.gainMeso(-money, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean hasShadowPartner = chr.getBuffedValue(MapleBuffStat.SHADOWPARTNER) != null;
|
||||
if (hasShadowPartner) {
|
||||
@@ -165,7 +178,8 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
else MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, bulletConsume, false, true);
|
||||
}
|
||||
}
|
||||
if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) {
|
||||
|
||||
if (projectile != 0 || soulArrow || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006 || attack.skill == 4111004) {
|
||||
int visProjectile = projectile; //visible projectile sent to players
|
||||
if (ItemConstants.isThrowingStar(projectile)) {
|
||||
MapleInventory cash = chr.getInventory(MapleInventoryType.CASH);
|
||||
@@ -178,10 +192,10 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else //bow, crossbow
|
||||
if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) {
|
||||
} else if (soulArrow || attack.skill == 3111004 || attack.skill == 3211004 || attack.skill == 11101004 || attack.skill == 15111007 || attack.skill == 14101006) {
|
||||
visProjectile = 0;
|
||||
}
|
||||
|
||||
byte[] packet;
|
||||
switch (attack.skill) {
|
||||
case 3121004: // Hurricane
|
||||
@@ -195,17 +209,7 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
|
||||
break;
|
||||
}
|
||||
chr.getMap().broadcastMessage(chr, packet, false, true);
|
||||
if (effect != null) {
|
||||
int money = effect.getMoneyCon();
|
||||
if (money != 0) {
|
||||
int moneyMod = money / 2;
|
||||
money += Randomizer.nextInt(moneyMod);
|
||||
if (money > chr.getMeso()) {
|
||||
money = chr.getMeso();
|
||||
}
|
||||
chr.gainMeso(-money, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (attack.skill != 0) {
|
||||
Skill skill = SkillFactory.getSkill(attack.skill);
|
||||
MapleStatEffect effect_ = skill.getEffect(chr.getSkillLevel(skill));
|
||||
|
||||
@@ -21,78 +21,20 @@
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
import java.io.File;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import client.SkillFactory;
|
||||
import client.processor.SpawnPetProcessor;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class SpawnPetHandler extends AbstractMaplePacketHandler {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
slea.readInt();
|
||||
byte slot = slea.readByte();
|
||||
slea.readByte();
|
||||
boolean lead = slea.readByte() == 1;
|
||||
MaplePet pet = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getPet();
|
||||
if (pet == null) return;
|
||||
|
||||
int petid = pet.getItemId();
|
||||
if (petid == 5000028 || petid == 5000047) //Handles Dragon AND Robos
|
||||
{
|
||||
if (chr.haveItem(petid + 1)) {
|
||||
chr.dropMessage(5, "You can't hatch your " + (petid == 5000028 ? "Dragon egg" : "Robo egg") + " if you already have a Baby " + (petid == 5000028 ? "Dragon." : "Robo."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else {
|
||||
int evolveid = MapleDataTool.getInt("info/evol1", dataRoot.getData("Pet/" + petid + ".img"));
|
||||
int petId = MaplePet.createPet(evolveid);
|
||||
if (petId == -1) {
|
||||
return;
|
||||
}
|
||||
long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
|
||||
MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
|
||||
pet.deleteFromDb();
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (chr.getPetIndex(pet) != -1) {
|
||||
chr.unequipPet(pet, true);
|
||||
} else {
|
||||
if (chr.getSkillLevel(SkillFactory.getSkill(8)) == 0 && chr.getPet(0) != null) {
|
||||
chr.unequipPet(chr.getPet(0), false);
|
||||
}
|
||||
if (lead) {
|
||||
chr.shiftPetsRight();
|
||||
}
|
||||
Point pos = chr.getPosition();
|
||||
pos.y -= 12;
|
||||
pet.setPos(pos);
|
||||
pet.setFh(chr.getMap().getFootholds().findBelow(pet.getPos()).getId());
|
||||
pet.setStance(0);
|
||||
pet.setSummoned(true);
|
||||
pet.saveToDb();
|
||||
chr.addPet(pet);
|
||||
chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
|
||||
c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
chr.commitExcludedItems();
|
||||
chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
|
||||
}
|
||||
SpawnPetProcessor.processSpawnPet(c, slot, lead);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -786,7 +786,7 @@ public class AbstractPlayerInteraction {
|
||||
}
|
||||
final Item newItem = MapleItemInformationProvider.getInstance().getEquipById(itemid);
|
||||
newItem.setPosition(slot);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addFromDB(newItem);
|
||||
c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).addItemFromDB(newItem);
|
||||
c.announce(MaplePacketCreator.modifyInventory(false, Collections.singletonList(new ModifyInventory(0, newItem))));
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,11 @@ public abstract class AbstractScriptManager {
|
||||
path = "scripts/" + path;
|
||||
engine = null;
|
||||
if (c != null) {
|
||||
engine = c.getScriptEngine(path);
|
||||
try {
|
||||
engine = c.getScriptEngine(path);
|
||||
} catch (NullPointerException npe) {
|
||||
c = null; // player disconnected
|
||||
}
|
||||
}
|
||||
if (engine == null) {
|
||||
File scriptFile = new File(path);
|
||||
|
||||
@@ -265,9 +265,12 @@ public class ReactorActionManager extends AbstractPlayerInteraction {
|
||||
MapleMonster mm = map.getMonsterById(id);
|
||||
if(mm != null) {
|
||||
int damage = (int)Math.ceil(mm.getMaxHp() / hitsToKill);
|
||||
MapleCharacter chr = this.getPlayer();
|
||||
|
||||
map.damageMonster(this.getPlayer(), mm, damage);
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(mm.getObjectId(), damage));
|
||||
if(chr != null) {
|
||||
map.damageMonster(chr, mm, damage);
|
||||
map.broadcastMessage(MaplePacketCreator.damageMonster(mm.getObjectId(), damage));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,12 @@ import client.status.MonsterStatus;
|
||||
import client.status.MonsterStatusEffect;
|
||||
import constants.ServerConstants;
|
||||
import constants.skills.Crusader;
|
||||
import constants.skills.DragonKnight;
|
||||
import constants.skills.FPMage;
|
||||
import constants.skills.Hermit;
|
||||
import constants.skills.ILMage;
|
||||
import constants.skills.NightLord;
|
||||
import constants.skills.NightWalker;
|
||||
import constants.skills.Priest;
|
||||
import constants.skills.Shadower;
|
||||
import constants.skills.WhiteKnight;
|
||||
import java.awt.Point;
|
||||
@@ -999,29 +1000,38 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
byte[] packet = MaplePacketCreator.cancelMonsterStatus(getObjectId(), oldEffect.getStati());
|
||||
map.broadcastMessage(packet, getPosition());
|
||||
|
||||
MapleCharacter controller = getController();
|
||||
if (controller != null && !controller.isMapObjectVisible(MapleMonster.this)) {
|
||||
controller.getClient().announce(packet);
|
||||
MapleCharacter chrController = getController();
|
||||
if (chrController != null && !chrController.isMapObjectVisible(MapleMonster.this)) {
|
||||
chrController.getClient().announce(packet);
|
||||
}
|
||||
stati.remove(stat);
|
||||
}
|
||||
}
|
||||
|
||||
public void debuffMob(int skillid) {
|
||||
MonsterStatus[] stats = {MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.MAGIC_ATTACK_UP};
|
||||
MonsterStatus[] statups = {MonsterStatus.WEAPON_ATTACK_UP, MonsterStatus.WEAPON_DEFENSE_UP, MonsterStatus.MAGIC_ATTACK_UP, MonsterStatus.MAGIC_DEFENSE_UP};
|
||||
statiLock.lock();
|
||||
try {
|
||||
int i = (skillid == Crusader.ARMOR_CRASH ? 1 : (skillid == WhiteKnight.MAGIC_CRASH ? 2 : 0));
|
||||
debuffMobStat(stats[i]);
|
||||
|
||||
if(ServerConstants.USE_ANTI_IMMUNITY_CRASH) {
|
||||
if (skillid == Crusader.ARMOR_CRASH) {
|
||||
if(!isBuffed(MonsterStatus.WEAPON_REFLECT)) debuffMobStat(MonsterStatus.WEAPON_IMMUNITY);
|
||||
if(!isBuffed(MonsterStatus.MAGIC_REFLECT)) debuffMobStat(MonsterStatus.MAGIC_IMMUNITY);
|
||||
} else if (skillid == WhiteKnight.MAGIC_CRASH) {
|
||||
if(!isBuffed(MonsterStatus.MAGIC_REFLECT)) debuffMobStat(MonsterStatus.MAGIC_IMMUNITY);
|
||||
} else {
|
||||
if(!isBuffed(MonsterStatus.WEAPON_REFLECT)) debuffMobStat(MonsterStatus.WEAPON_IMMUNITY);
|
||||
if(skillid == Hermit.SHADOW_MESO) {
|
||||
debuffMobStat(statups[1]);
|
||||
debuffMobStat(statups[3]);
|
||||
} else if(skillid == Priest.DISPEL) {
|
||||
for(MonsterStatus ms : statups) {
|
||||
debuffMobStat(ms);
|
||||
}
|
||||
} else { // is a crash skill
|
||||
int i = (skillid == Crusader.ARMOR_CRASH ? 1 : (skillid == WhiteKnight.MAGIC_CRASH ? 2 : 0));
|
||||
debuffMobStat(statups[i]);
|
||||
|
||||
if(ServerConstants.USE_ANTI_IMMUNITY_CRASH) {
|
||||
if (skillid == Crusader.ARMOR_CRASH) {
|
||||
if(!isBuffed(MonsterStatus.WEAPON_REFLECT)) debuffMobStat(MonsterStatus.WEAPON_IMMUNITY);
|
||||
if(!isBuffed(MonsterStatus.MAGIC_REFLECT)) debuffMobStat(MonsterStatus.MAGIC_IMMUNITY);
|
||||
} else if (skillid == WhiteKnight.MAGIC_CRASH) {
|
||||
if(!isBuffed(MonsterStatus.MAGIC_REFLECT)) debuffMobStat(MonsterStatus.MAGIC_IMMUNITY);
|
||||
} else {
|
||||
if(!isBuffed(MonsterStatus.WEAPON_REFLECT)) debuffMobStat(MonsterStatus.WEAPON_IMMUNITY);
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -518,6 +518,9 @@ public class MapleQuest {
|
||||
case SKILL:
|
||||
ret = new SkillAction(this, data);
|
||||
break;
|
||||
case PETTAMENESS:
|
||||
ret = new PetTamenessAction(this, data);
|
||||
break;
|
||||
default:
|
||||
//FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, "Unhandled Action Type: " + type.toString() + " QuestID: " + this.getId());
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,7 @@ package server.quest;
|
||||
* @author Matze
|
||||
*/
|
||||
public enum MapleQuestActionType {
|
||||
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), ZERO(14);
|
||||
UNDEFINED(-1), EXP(0), ITEM(1), NEXTQUEST(2), MESO(3), QUEST(4), SKILL(5), FAME(6), BUFF(7), PETSKILL(8), YES(9), NO(10), NPC(11), MIN_LEVEL(12), NORMAL_AUTO_START(13), PETTAMENESS(14), ZERO(15);
|
||||
final byte type;
|
||||
|
||||
private MapleQuestActionType(int type) {
|
||||
@@ -60,6 +60,8 @@ public enum MapleQuestActionType {
|
||||
return MIN_LEVEL;
|
||||
} else if (name.equals("normalAutoStart")) {
|
||||
return NORMAL_AUTO_START;
|
||||
} else if (name.equals("pettameness")) {
|
||||
return PETTAMENESS;
|
||||
} else if (name.equals("0")) {
|
||||
return ZERO;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
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 server.quest.actions;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.MaplePet;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataTool;
|
||||
import server.quest.MapleQuest;
|
||||
import server.quest.MapleQuestActionType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class PetTamenessAction extends MapleQuestAction {
|
||||
int tameness;
|
||||
|
||||
public PetTamenessAction(MapleQuest quest, MapleData data) {
|
||||
super(MapleQuestActionType.PETTAMENESS, quest);
|
||||
questID = quest.getId();
|
||||
processData(data);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void processData(MapleData data) {
|
||||
tameness = MapleDataTool.getInt(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(MapleCharacter chr, Integer extSelection) {
|
||||
MapleClient c = chr.getClient();
|
||||
|
||||
MaplePet pet = chr.getPet(0); // assuming here only the pet leader will gain tameness
|
||||
if(pet == null) return;
|
||||
|
||||
c.lockClient();
|
||||
try {
|
||||
pet.gainClosenessFullness(chr, tameness, 0, 0);
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user