Switch to Maven file structure
This commit is contained in:
668
src/main/java/client/inventory/Equip.java
Normal file
668
src/main/java/client/inventory/Equip.java
Normal file
@@ -0,0 +1,668 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
import client.MapleClient;
|
||||
import config.YamlConfig;
|
||||
import constants.game.ExpTable;
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
|
||||
public class Equip extends Item {
|
||||
|
||||
public static enum ScrollResult {
|
||||
|
||||
FAIL(0), SUCCESS(1), CURSE(2);
|
||||
private int value = -1;
|
||||
|
||||
private ScrollResult(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum StatUpgrade {
|
||||
|
||||
incDEX(0), incSTR(1), incINT(2), incLUK(3),
|
||||
incMHP(4), incMMP(5), incPAD(6), incMAD(7),
|
||||
incPDD(8), incMDD(9), incEVA(10), incACC(11),
|
||||
incSpeed(12), incJump(13), incVicious(14), incSlot(15);
|
||||
private int value = -1;
|
||||
|
||||
private StatUpgrade(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private byte upgradeSlots;
|
||||
private byte level, itemLevel;
|
||||
private short flag;
|
||||
private short str, dex, _int, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, vicious;
|
||||
private float itemExp;
|
||||
private int ringid = -1;
|
||||
private boolean wear = false;
|
||||
private boolean isUpgradeable, isElemental = false; // timeless or reverse, or any equip that could levelup on GMS for all effects
|
||||
|
||||
public Equip(int id, short position) {
|
||||
this(id, position, 0);
|
||||
}
|
||||
|
||||
public Equip(int id, short position, int slots) {
|
||||
super(id, position, (short) 1);
|
||||
this.upgradeSlots = (byte) slots;
|
||||
this.itemExp = 0;
|
||||
this.itemLevel = 1;
|
||||
|
||||
this.isElemental = (MapleItemInformationProvider.getInstance().getEquipLevel(id, false) > 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item copy() {
|
||||
Equip ret = new Equip(getItemId(), getPosition(), getUpgradeSlots());
|
||||
ret.str = str;
|
||||
ret.dex = dex;
|
||||
ret._int = _int;
|
||||
ret.luk = luk;
|
||||
ret.hp = hp;
|
||||
ret.mp = mp;
|
||||
ret.matk = matk;
|
||||
ret.mdef = mdef;
|
||||
ret.watk = watk;
|
||||
ret.wdef = wdef;
|
||||
ret.acc = acc;
|
||||
ret.avoid = avoid;
|
||||
ret.hands = hands;
|
||||
ret.speed = speed;
|
||||
ret.jump = jump;
|
||||
ret.flag = flag;
|
||||
ret.vicious = vicious;
|
||||
ret.upgradeSlots = upgradeSlots;
|
||||
ret.itemLevel = itemLevel;
|
||||
ret.itemExp = itemExp;
|
||||
ret.level = level;
|
||||
ret.log = new LinkedList<>(log);
|
||||
ret.setOwner(getOwner());
|
||||
ret.setQuantity(getQuantity());
|
||||
ret.setExpiration(getExpiration());
|
||||
ret.setGiftFrom(getGiftFrom());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getItemType() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public byte getUpgradeSlots() {
|
||||
return upgradeSlots;
|
||||
}
|
||||
|
||||
public short getStr() {
|
||||
return str;
|
||||
}
|
||||
|
||||
public short getDex() {
|
||||
return dex;
|
||||
}
|
||||
|
||||
public short getInt() {
|
||||
return _int;
|
||||
}
|
||||
|
||||
public short getLuk() {
|
||||
return luk;
|
||||
}
|
||||
|
||||
public short getHp() {
|
||||
return hp;
|
||||
}
|
||||
|
||||
public short getMp() {
|
||||
return mp;
|
||||
}
|
||||
|
||||
public short getWatk() {
|
||||
return watk;
|
||||
}
|
||||
|
||||
public short getMatk() {
|
||||
return matk;
|
||||
}
|
||||
|
||||
public short getWdef() {
|
||||
return wdef;
|
||||
}
|
||||
|
||||
public short getMdef() {
|
||||
return mdef;
|
||||
}
|
||||
|
||||
public short getAcc() {
|
||||
return acc;
|
||||
}
|
||||
|
||||
public short getAvoid() {
|
||||
return avoid;
|
||||
}
|
||||
|
||||
public short getHands() {
|
||||
return hands;
|
||||
}
|
||||
|
||||
public short getSpeed() {
|
||||
return speed;
|
||||
}
|
||||
|
||||
public short getJump() {
|
||||
return jump;
|
||||
}
|
||||
|
||||
public short getVicious() {
|
||||
return vicious;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlag(short flag) {
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public void setStr(short str) {
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public void setDex(short dex) {
|
||||
this.dex = dex;
|
||||
}
|
||||
|
||||
public void setInt(short _int) {
|
||||
this._int = _int;
|
||||
}
|
||||
|
||||
public void setLuk(short luk) {
|
||||
this.luk = luk;
|
||||
}
|
||||
|
||||
public void setHp(short hp) {
|
||||
this.hp = hp;
|
||||
}
|
||||
|
||||
public void setMp(short mp) {
|
||||
this.mp = mp;
|
||||
}
|
||||
|
||||
public void setWatk(short watk) {
|
||||
this.watk = watk;
|
||||
}
|
||||
|
||||
public void setMatk(short matk) {
|
||||
this.matk = matk;
|
||||
}
|
||||
|
||||
public void setWdef(short wdef) {
|
||||
this.wdef = wdef;
|
||||
}
|
||||
|
||||
public void setMdef(short mdef) {
|
||||
this.mdef = mdef;
|
||||
}
|
||||
|
||||
public void setAcc(short acc) {
|
||||
this.acc = acc;
|
||||
}
|
||||
|
||||
public void setAvoid(short avoid) {
|
||||
this.avoid = avoid;
|
||||
}
|
||||
|
||||
public void setHands(short hands) {
|
||||
this.hands = hands;
|
||||
}
|
||||
|
||||
public void setSpeed(short speed) {
|
||||
this.speed = speed;
|
||||
}
|
||||
|
||||
public void setJump(short jump) {
|
||||
this.jump = jump;
|
||||
}
|
||||
|
||||
public void setVicious(short vicious) {
|
||||
this.vicious = vicious;
|
||||
}
|
||||
|
||||
public void setUpgradeSlots(byte upgradeSlots) {
|
||||
this.upgradeSlots = upgradeSlots;
|
||||
}
|
||||
|
||||
public byte getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(byte level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
private static int getStatModifier(boolean isAttribute) {
|
||||
// each set of stat points grants a chance for a bonus stat point upgrade at equip level up.
|
||||
|
||||
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_POWER) {
|
||||
if(isAttribute) return 2;
|
||||
else return 4;
|
||||
}
|
||||
else {
|
||||
if(isAttribute) return 4;
|
||||
else return 16;
|
||||
}
|
||||
}
|
||||
|
||||
private static int randomizeStatUpgrade(int top) {
|
||||
int limit = Math.min(top, YamlConfig.config.server.MAX_EQUIPMNT_LVLUP_STAT_UP);
|
||||
|
||||
int poolCount = (limit * (limit + 1) / 2) + limit;
|
||||
int rnd = Randomizer.rand(0, poolCount);
|
||||
|
||||
int stat = 0;
|
||||
if(rnd >= limit) {
|
||||
rnd -= limit;
|
||||
stat = 1 + (int)Math.floor((-1 + Math.sqrt((8 * rnd) + 1)) / 2); // optimized randomizeStatUpgrade author: David A.
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
private static boolean isPhysicalWeapon(int itemid) {
|
||||
Equip eqp = (Equip) MapleItemInformationProvider.getInstance().getEquipById(itemid);
|
||||
return eqp.getWatk() >= eqp.getMatk();
|
||||
}
|
||||
|
||||
private boolean isNotWeaponAffinity(StatUpgrade name) {
|
||||
// Vcoc's idea - WATK/MATK expected gains lessens outside of weapon affinity (physical/magic)
|
||||
|
||||
if (ItemConstants.isWeapon(this.getItemId())) {
|
||||
if (name.equals(StatUpgrade.incPAD)) {
|
||||
if (!isPhysicalWeapon(this.getItemId())) {
|
||||
return true;
|
||||
}
|
||||
} else if (name.equals(StatUpgrade.incMAD)) {
|
||||
if (isPhysicalWeapon(this.getItemId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void getUnitStatUpgrade(List<Pair<StatUpgrade, Integer>> stats, StatUpgrade name, int curStat, boolean isAttribute) {
|
||||
isUpgradeable = true;
|
||||
|
||||
int maxUpgrade = randomizeStatUpgrade((int)(1 + (curStat / (getStatModifier(isAttribute) * (isNotWeaponAffinity(name) ? 2.7 : 1)))));
|
||||
if(maxUpgrade == 0) return;
|
||||
|
||||
stats.add(new Pair<>(name, maxUpgrade));
|
||||
}
|
||||
|
||||
private static void getUnitSlotUpgrade(List<Pair<StatUpgrade, Integer>> stats, StatUpgrade name) {
|
||||
if(Math.random() < 0.1) {
|
||||
stats.add(new Pair<>(name, 1)); // 10% success on getting a slot upgrade.
|
||||
}
|
||||
}
|
||||
|
||||
private void improveDefaultStats(List<Pair<StatUpgrade, Integer>> stats) {
|
||||
if(dex > 0) getUnitStatUpgrade(stats, StatUpgrade.incDEX, dex, true);
|
||||
if(str > 0) getUnitStatUpgrade(stats, StatUpgrade.incSTR, str, true);
|
||||
if(_int > 0) getUnitStatUpgrade(stats, StatUpgrade.incINT,_int, true);
|
||||
if(luk > 0) getUnitStatUpgrade(stats, StatUpgrade.incLUK, luk, true);
|
||||
if(hp > 0) getUnitStatUpgrade(stats, StatUpgrade.incMHP, hp, false);
|
||||
if(mp > 0) getUnitStatUpgrade(stats, StatUpgrade.incMMP, mp, false);
|
||||
if(watk > 0) getUnitStatUpgrade(stats, StatUpgrade.incPAD, watk, false);
|
||||
if(matk > 0) getUnitStatUpgrade(stats, StatUpgrade.incMAD, matk, false);
|
||||
if(wdef > 0) getUnitStatUpgrade(stats, StatUpgrade.incPDD, wdef, false);
|
||||
if(mdef > 0) getUnitStatUpgrade(stats, StatUpgrade.incMDD, mdef, false);
|
||||
if(avoid > 0) getUnitStatUpgrade(stats, StatUpgrade.incEVA, avoid, false);
|
||||
if(acc > 0) getUnitStatUpgrade(stats, StatUpgrade.incACC, acc, false);
|
||||
if(speed > 0) getUnitStatUpgrade(stats, StatUpgrade.incSpeed, speed, false);
|
||||
if(jump > 0) getUnitStatUpgrade(stats, StatUpgrade.incJump, jump, false);
|
||||
}
|
||||
|
||||
public Map<StatUpgrade, Short> getStats() {
|
||||
Map<StatUpgrade, Short> stats = new HashMap<>(5);
|
||||
|
||||
if(dex > 0) stats.put(StatUpgrade.incDEX, dex);
|
||||
if(str > 0) stats.put(StatUpgrade.incSTR, str);
|
||||
if(_int > 0) stats.put(StatUpgrade.incINT,_int);
|
||||
if(luk > 0) stats.put(StatUpgrade.incLUK, luk);
|
||||
if(hp > 0) stats.put(StatUpgrade.incMHP, hp);
|
||||
if(mp > 0) stats.put(StatUpgrade.incMMP, mp);
|
||||
if(watk > 0) stats.put(StatUpgrade.incPAD, watk);
|
||||
if(matk > 0) stats.put(StatUpgrade.incMAD, matk);
|
||||
if(wdef > 0) stats.put(StatUpgrade.incPDD, wdef);
|
||||
if(mdef > 0) stats.put(StatUpgrade.incMDD, mdef);
|
||||
if(avoid > 0) stats.put(StatUpgrade.incEVA, avoid);
|
||||
if(acc > 0) stats.put(StatUpgrade.incACC, acc);
|
||||
if(speed > 0) stats.put(StatUpgrade.incSpeed, speed);
|
||||
if(jump > 0) stats.put(StatUpgrade.incJump, jump);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public Pair<String, Pair<Boolean, Boolean>> gainStats(List<Pair<StatUpgrade, Integer>> stats) {
|
||||
boolean gotSlot = false, gotVicious = false;
|
||||
String lvupStr = "";
|
||||
Integer statUp, maxStat = YamlConfig.config.server.MAX_EQUIPMNT_STAT;
|
||||
for (Pair<StatUpgrade, Integer> stat : stats) {
|
||||
switch (stat.getLeft()) {
|
||||
case incDEX:
|
||||
statUp = Math.min(stat.getRight(), maxStat - dex);
|
||||
dex += statUp;
|
||||
lvupStr += "+" + statUp + "DEX ";
|
||||
break;
|
||||
case incSTR:
|
||||
statUp = Math.min(stat.getRight(), maxStat - str);
|
||||
str += statUp;
|
||||
lvupStr += "+" + statUp + "STR ";
|
||||
break;
|
||||
case incINT:
|
||||
statUp = Math.min(stat.getRight(), maxStat - _int);
|
||||
_int += statUp;
|
||||
lvupStr += "+" + statUp + "INT ";
|
||||
break;
|
||||
case incLUK:
|
||||
statUp = Math.min(stat.getRight(), maxStat - luk);
|
||||
luk += statUp;
|
||||
lvupStr += "+" + statUp + "LUK ";
|
||||
break;
|
||||
case incMHP:
|
||||
statUp = Math.min(stat.getRight(), maxStat - hp);
|
||||
hp += statUp;
|
||||
lvupStr += "+" + statUp + "HP ";
|
||||
break;
|
||||
case incMMP:
|
||||
statUp = Math.min(stat.getRight(), maxStat - mp);
|
||||
mp += statUp;
|
||||
lvupStr += "+" + statUp + "MP ";
|
||||
break;
|
||||
case incPAD:
|
||||
statUp = Math.min(stat.getRight(), maxStat - watk);
|
||||
watk += statUp;
|
||||
lvupStr += "+" + statUp + "WATK ";
|
||||
break;
|
||||
case incMAD:
|
||||
statUp = Math.min(stat.getRight(), maxStat - matk);
|
||||
matk += statUp;
|
||||
lvupStr += "+" + statUp + "MATK ";
|
||||
break;
|
||||
case incPDD:
|
||||
statUp = Math.min(stat.getRight(), maxStat - wdef);
|
||||
wdef += statUp;
|
||||
lvupStr += "+" + statUp + "WDEF ";
|
||||
break;
|
||||
case incMDD:
|
||||
statUp = Math.min(stat.getRight(), maxStat - mdef);
|
||||
mdef += statUp;
|
||||
lvupStr += "+" + statUp + "MDEF ";
|
||||
break;
|
||||
case incEVA:
|
||||
statUp = Math.min(stat.getRight(), maxStat - avoid);
|
||||
avoid += statUp;
|
||||
lvupStr += "+" + statUp + "AVOID ";
|
||||
break;
|
||||
case incACC:
|
||||
statUp = Math.min(stat.getRight(), maxStat - acc);
|
||||
acc += statUp;
|
||||
lvupStr += "+" + statUp + "ACC ";
|
||||
break;
|
||||
case incSpeed:
|
||||
statUp = Math.min(stat.getRight(), maxStat - speed);
|
||||
speed += statUp;
|
||||
lvupStr += "+" + statUp + "SPEED ";
|
||||
break;
|
||||
case incJump:
|
||||
statUp = Math.min(stat.getRight(), maxStat - jump);
|
||||
jump += statUp;
|
||||
lvupStr += "+" + statUp + "JUMP ";
|
||||
break;
|
||||
|
||||
case incVicious:
|
||||
vicious -= stat.getRight();
|
||||
gotVicious = true;
|
||||
break;
|
||||
case incSlot:
|
||||
upgradeSlots += stat.getRight();
|
||||
gotSlot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Pair<>(lvupStr, new Pair<>(gotSlot, gotVicious));
|
||||
}
|
||||
|
||||
private void gainLevel(MapleClient c) {
|
||||
List<Pair<StatUpgrade, Integer>> stats = new LinkedList<>();
|
||||
|
||||
if(isElemental) {
|
||||
List<Pair<String, Integer>> elementalStats = MapleItemInformationProvider.getInstance().getItemLevelupStats(getItemId(), itemLevel);
|
||||
|
||||
for(Pair<String, Integer> p: elementalStats) {
|
||||
if(p.getRight() > 0) stats.add(new Pair<>(StatUpgrade.valueOf(p.getLeft()), p.getRight()));
|
||||
}
|
||||
}
|
||||
|
||||
if(!stats.isEmpty()) {
|
||||
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
|
||||
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
|
||||
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
|
||||
}
|
||||
} else {
|
||||
isUpgradeable = false;
|
||||
|
||||
improveDefaultStats(stats);
|
||||
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
|
||||
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
|
||||
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
|
||||
}
|
||||
|
||||
if(isUpgradeable) {
|
||||
while(stats.isEmpty()) {
|
||||
improveDefaultStats(stats);
|
||||
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
|
||||
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
|
||||
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemLevel++;
|
||||
|
||||
String lvupStr = "'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "' is now level " + itemLevel + "! ";
|
||||
String showStr = "#e'" + MapleItemInformationProvider.getInstance().getName(this.getItemId()) + "'#b is now #elevel #r" + itemLevel + "#k#b!";
|
||||
|
||||
Pair<String, Pair<Boolean, Boolean>> res = this.gainStats(stats);
|
||||
lvupStr += res.getLeft();
|
||||
boolean gotSlot = res.getRight().getLeft();
|
||||
boolean gotVicious = res.getRight().getRight();
|
||||
|
||||
if (gotVicious) {
|
||||
//c.getPlayer().dropMessage(6, "A new Vicious Hammer opportunity has been found on the '" + MapleItemInformationProvider.getInstance().getName(getItemId()) + "'!");
|
||||
lvupStr += "+VICIOUS ";
|
||||
}
|
||||
if (gotSlot) {
|
||||
//c.getPlayer().dropMessage(6, "A new upgrade slot has been found on the '" + MapleItemInformationProvider.getInstance().getName(getItemId()) + "'!");
|
||||
lvupStr += "+UPGSLOT ";
|
||||
}
|
||||
|
||||
c.getPlayer().equipChanged();
|
||||
|
||||
showLevelupMessage(showStr, c); // thanks to Polaris dev team !
|
||||
c.getPlayer().dropMessage(6, lvupStr);
|
||||
|
||||
c.announce(MaplePacketCreator.showEquipmentLevelUp());
|
||||
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignEffect(c.getPlayer().getId(), 15));
|
||||
c.getPlayer().forceUpdateItem(this);
|
||||
}
|
||||
|
||||
public int getItemExp() {
|
||||
return (int) itemExp;
|
||||
}
|
||||
|
||||
private static double normalizedMasteryExp(int reqLevel) {
|
||||
// Conversion factor between mob exp and equip exp gain. Through many calculations, the expected for equipment levelup
|
||||
// from level 1 to 2 is killing about 100~200 mobs of the same level range, on a 1x EXP rate scenario.
|
||||
|
||||
if(reqLevel < 5) {
|
||||
return 42;
|
||||
} else if(reqLevel >= 78) {
|
||||
return Math.max((10413.648 * Math.exp(reqLevel * 0.03275)), 15);
|
||||
} else if(reqLevel >= 38) {
|
||||
return Math.max(( 4985.818 * Math.exp(reqLevel * 0.02007)), 15);
|
||||
} else if(reqLevel >= 18) {
|
||||
return Math.max(( 248.219 * Math.exp(reqLevel * 0.11093)), 15);
|
||||
} else {
|
||||
return Math.max(((1334.564 * Math.log(reqLevel)) - 1731.976), 15);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void gainItemExp(MapleClient c, int gain) { // Ronan's Equip Exp gain method
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if(!ii.isUpgradeable(this.getItemId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
int equipMaxLevel = Math.min(30, Math.max(ii.getEquipLevel(this.getItemId(), true), YamlConfig.config.server.USE_EQUIPMNT_LVLUP));
|
||||
if (itemLevel >= equipMaxLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
int reqLevel = ii.getEquipLevelReq(this.getItemId());
|
||||
|
||||
float masteryModifier = (float)(YamlConfig.config.server.EQUIP_EXP_RATE * ExpTable.getExpNeededForLevel(1)) / (float)normalizedMasteryExp(reqLevel);
|
||||
float elementModifier = (isElemental) ? 0.85f : 0.6f;
|
||||
|
||||
float baseExpGain = gain * elementModifier * masteryModifier;
|
||||
|
||||
itemExp += baseExpGain;
|
||||
int expNeeded = ExpTable.getEquipExpNeededForLevel(itemLevel);
|
||||
|
||||
if(YamlConfig.config.server.USE_DEBUG_SHOW_INFO_EQPEXP) System.out.println("'" + ii.getName(this.getItemId()) + "' -> EXP Gain: " + gain + " Mastery: " + masteryModifier + " Base gain: " + baseExpGain + " exp: " + itemExp + " / " + expNeeded + ", Kills TNL: " + expNeeded / (baseExpGain / c.getPlayer().getExpRate()));
|
||||
|
||||
if (itemExp >= expNeeded) {
|
||||
while(itemExp >= expNeeded) {
|
||||
itemExp -= expNeeded;
|
||||
gainLevel(c);
|
||||
|
||||
if(itemLevel >= equipMaxLevel) {
|
||||
itemExp = 0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
expNeeded = ExpTable.getEquipExpNeededForLevel(itemLevel);
|
||||
}
|
||||
}
|
||||
|
||||
c.getPlayer().forceUpdateItem(this);
|
||||
//if(YamlConfig.config.server.USE_DEBUG) c.getPlayer().dropMessage("'" + ii.getName(this.getItemId()) + "': " + itemExp + " / " + expNeeded);
|
||||
}
|
||||
|
||||
private boolean reachedMaxLevel() {
|
||||
if (isElemental) {
|
||||
if (itemLevel < MapleItemInformationProvider.getInstance().getEquipLevel(getItemId(), true)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return itemLevel >= YamlConfig.config.server.USE_EQUIPMNT_LVLUP;
|
||||
}
|
||||
|
||||
public String showEquipFeatures(MapleClient c) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if(!ii.isUpgradeable(this.getItemId())) return "";
|
||||
|
||||
String eqpName = ii.getName(getItemId());
|
||||
String eqpInfo = reachedMaxLevel() ? " #e#rMAX LEVEL#k#n" : (" EXP: #e#b" + (int)itemExp + "#k#n / " + ExpTable.getEquipExpNeededForLevel(itemLevel));
|
||||
|
||||
return "'" + eqpName + "' -> LV: #e#b" + itemLevel + "#k#n " + eqpInfo + "\r\n";
|
||||
}
|
||||
|
||||
private static void showLevelupMessage(String msg, MapleClient c) {
|
||||
c.getPlayer().showHint(msg, 300);
|
||||
}
|
||||
|
||||
public void setItemExp(int exp) {
|
||||
this.itemExp = exp;
|
||||
}
|
||||
|
||||
public void setItemLevel(byte level) {
|
||||
this.itemLevel = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setQuantity(short quantity) {
|
||||
if (quantity < 0 || quantity > 1) {
|
||||
throw new RuntimeException("Setting the quantity to " + quantity + " on an equip (itemid: " + getItemId() + ")");
|
||||
}
|
||||
super.setQuantity(quantity);
|
||||
}
|
||||
|
||||
public void setUpgradeSlots(int i) {
|
||||
this.upgradeSlots = (byte) i;
|
||||
}
|
||||
|
||||
public void setVicious(int i) {
|
||||
this.vicious = (short) i;
|
||||
}
|
||||
|
||||
public int getRingId() {
|
||||
return ringid;
|
||||
}
|
||||
|
||||
public void setRingId(int id) {
|
||||
this.ringid = id;
|
||||
}
|
||||
|
||||
public boolean isWearing() {
|
||||
return wear;
|
||||
}
|
||||
|
||||
public void wear(boolean yes) {
|
||||
wear = yes;
|
||||
}
|
||||
|
||||
public byte getItemLevel() {
|
||||
return itemLevel;
|
||||
}
|
||||
}
|
||||
193
src/main/java/client/inventory/Item.java
Normal file
193
src/main/java/client/inventory/Item.java
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import server.MapleItemInformationProvider;
|
||||
|
||||
public class Item implements Comparable<Item> {
|
||||
|
||||
private static AtomicInteger runningCashId = new AtomicInteger(777000000); // pets & rings shares cashid values
|
||||
|
||||
private int id, cashId, sn;
|
||||
private short position;
|
||||
private short quantity;
|
||||
private int petid = -1;
|
||||
private MaplePet pet = null;
|
||||
private String owner = "";
|
||||
protected List<String> log;
|
||||
private short flag;
|
||||
private long expiration = -1;
|
||||
private String giftFrom = "";
|
||||
|
||||
public Item(int id, short position, short quantity) {
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
this.quantity = quantity;
|
||||
this.log = new LinkedList<>();
|
||||
this.flag = 0;
|
||||
}
|
||||
|
||||
public Item(int id, short position, short quantity, int petid) {
|
||||
this.id = id;
|
||||
this.position = position;
|
||||
this.quantity = quantity;
|
||||
if (petid > -1) { // issue with null "pet" having petid > -1 found thanks to MedicOP
|
||||
this.pet = MaplePet.loadFromDb(id, position, petid);
|
||||
if (this.pet == null) {
|
||||
petid = -1;
|
||||
}
|
||||
}
|
||||
this.petid = petid;
|
||||
this.flag = 0;
|
||||
this.log = new LinkedList<>();
|
||||
}
|
||||
|
||||
public Item copy() {
|
||||
Item ret = new Item(id, position, quantity, petid);
|
||||
ret.flag = flag;
|
||||
ret.owner = owner;
|
||||
ret.expiration = expiration;
|
||||
ret.log = new LinkedList<>(log);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void setPosition(short position) {
|
||||
this.position = position;
|
||||
if (this.pet != null) this.pet.setPosition(position);
|
||||
}
|
||||
|
||||
public void setQuantity(short quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getCashId() {
|
||||
if (cashId == 0) {
|
||||
cashId = runningCashId.getAndIncrement();
|
||||
}
|
||||
return cashId;
|
||||
}
|
||||
|
||||
public short getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public short getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public MapleInventoryType getInventoryType() {
|
||||
return ItemConstants.getInventoryType(id);
|
||||
}
|
||||
|
||||
public byte getItemType() { // 1: equip, 3: pet, 2: other
|
||||
if (getPetId() > -1) {
|
||||
return 3;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public int getPetId() {
|
||||
return petid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Item other) {
|
||||
if (this.id < other.getItemId()) {
|
||||
return -1;
|
||||
} else if (this.id > other.getItemId()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Item: " + id + " quantity: " + quantity;
|
||||
}
|
||||
|
||||
public List<String> getLog() {
|
||||
return Collections.unmodifiableList(log);
|
||||
}
|
||||
|
||||
public short getFlag() {
|
||||
return flag;
|
||||
}
|
||||
|
||||
public void setFlag(short b) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (ii.isAccountRestricted(id)) {
|
||||
b |= ItemConstants.ACCOUNT_SHARING; // thanks Shinigami15 for noticing ACCOUNT_SHARING flag not being applied properly to items server-side
|
||||
}
|
||||
|
||||
this.flag = b;
|
||||
}
|
||||
|
||||
public long getExpiration() {
|
||||
return expiration;
|
||||
}
|
||||
|
||||
public void setExpiration(long expire) {
|
||||
this.expiration = !ItemConstants.isPermanentItem(id) ? expire : ItemConstants.isPet(id) ? Long.MAX_VALUE : -1;
|
||||
}
|
||||
|
||||
public int getSN() {
|
||||
return sn;
|
||||
}
|
||||
|
||||
public void setSN(int sn) {
|
||||
this.sn = sn;
|
||||
}
|
||||
|
||||
public String getGiftFrom() {
|
||||
return giftFrom;
|
||||
}
|
||||
|
||||
public void setGiftFrom(String giftFrom) {
|
||||
this.giftFrom = giftFrom;
|
||||
}
|
||||
|
||||
public MaplePet getPet() {
|
||||
return pet;
|
||||
}
|
||||
|
||||
public boolean isUntradeable() {
|
||||
return ((this.getFlag() & ItemConstants.UNTRADEABLE) == ItemConstants.UNTRADEABLE) || (MapleItemInformationProvider.getInstance().isDropRestricted(this.getItemId()) && !MapleKarmaManipulator.hasKarmaFlag(this));
|
||||
}
|
||||
}
|
||||
487
src/main/java/client/inventory/ItemFactory.java
Normal file
487
src/main/java/client/inventory/ItemFactory.java
Normal file
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
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 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.inventory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Flav
|
||||
*/
|
||||
public enum ItemFactory {
|
||||
|
||||
INVENTORY(1, false),
|
||||
STORAGE(2, true),
|
||||
CASH_EXPLORER(3, true),
|
||||
CASH_CYGNUS(4, true),
|
||||
CASH_ARAN(5, true),
|
||||
MERCHANT(6, false),
|
||||
CASH_OVERALL(7, true),
|
||||
MARRIAGE_GIFTS(8, false),
|
||||
DUEY(9, false);
|
||||
private final int value;
|
||||
private final boolean account;
|
||||
|
||||
private static final int lockCount = 400;
|
||||
private static final Lock locks[] = new Lock[lockCount]; // thanks Masterrulax for pointing out a bottleneck issue here
|
||||
|
||||
static {
|
||||
for (int i = 0; i < lockCount; i++) {
|
||||
locks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.ITEM, true);
|
||||
}
|
||||
}
|
||||
|
||||
private ItemFactory(int value, boolean account) {
|
||||
this.value = value;
|
||||
this.account = account;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public List<Pair<Item, MapleInventoryType>> loadItems(int id, boolean login) throws SQLException {
|
||||
if(value != 6) return loadItemsCommon(id, login);
|
||||
else return loadItemsMerchant(id, login);
|
||||
}
|
||||
|
||||
public void saveItems(List<Pair<Item, MapleInventoryType>> items, int id, Connection con) throws SQLException {
|
||||
saveItems(items, null, id, con);
|
||||
}
|
||||
|
||||
public void saveItems(List<Pair<Item, MapleInventoryType>> items, List<Short> bundlesList, int id, Connection con) throws SQLException {
|
||||
// thanks Arufonsu, MedicOP, BHB for pointing a "synchronized" bottleneck here
|
||||
|
||||
if(value != 6) saveItemsCommon(items, id, con);
|
||||
else saveItemsMerchant(items, bundlesList, id, con);
|
||||
}
|
||||
|
||||
private static Equip loadEquipFromResultSet(ResultSet rs) throws SQLException {
|
||||
Equip equip = new Equip(rs.getInt("itemid"), (short) rs.getInt("position"));
|
||||
equip.setOwner(rs.getString("owner"));
|
||||
equip.setQuantity((short) rs.getInt("quantity"));
|
||||
equip.setAcc((short) rs.getInt("acc"));
|
||||
equip.setAvoid((short) rs.getInt("avoid"));
|
||||
equip.setDex((short) rs.getInt("dex"));
|
||||
equip.setHands((short) rs.getInt("hands"));
|
||||
equip.setHp((short) rs.getInt("hp"));
|
||||
equip.setInt((short) rs.getInt("int"));
|
||||
equip.setJump((short) rs.getInt("jump"));
|
||||
equip.setVicious((short) rs.getInt("vicious"));
|
||||
equip.setFlag((short) rs.getInt("flag"));
|
||||
equip.setLuk((short) rs.getInt("luk"));
|
||||
equip.setMatk((short) rs.getInt("matk"));
|
||||
equip.setMdef((short) rs.getInt("mdef"));
|
||||
equip.setMp((short) rs.getInt("mp"));
|
||||
equip.setSpeed((short) rs.getInt("speed"));
|
||||
equip.setStr((short) rs.getInt("str"));
|
||||
equip.setWatk((short) rs.getInt("watk"));
|
||||
equip.setWdef((short) rs.getInt("wdef"));
|
||||
equip.setUpgradeSlots((byte) rs.getInt("upgradeslots"));
|
||||
equip.setLevel((byte) rs.getByte("level"));
|
||||
equip.setItemExp(rs.getInt("itemexp"));
|
||||
equip.setItemLevel(rs.getByte("itemlevel"));
|
||||
equip.setExpiration(rs.getLong("expiration"));
|
||||
equip.setGiftFrom(rs.getString("giftFrom"));
|
||||
equip.setRingId(rs.getInt("ringid"));
|
||||
|
||||
return equip;
|
||||
}
|
||||
|
||||
public static List<Pair<Item, Integer>> loadEquippedItems(int id, boolean isAccount, boolean login) throws SQLException {
|
||||
List<Pair<Item, Integer>> items = new ArrayList<>();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM ");
|
||||
query.append("(SELECT id, accountid FROM characters) AS accountterm ");
|
||||
query.append("RIGHT JOIN ");
|
||||
query.append("(SELECT * FROM (`inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`))) AS equipterm");
|
||||
query.append(" ON accountterm.id=equipterm.characterid ");
|
||||
query.append("WHERE accountterm.`");
|
||||
query.append(isAccount ? "accountid" : "characterid");
|
||||
query.append("` = ?");
|
||||
query.append(login ? " AND `inventorytype` = " + MapleInventoryType.EQUIPPED.getType() : "");
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement(query.toString())) {
|
||||
ps.setInt(1, id);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
Integer cid = rs.getInt("characterid");
|
||||
items.add(new Pair<Item, Integer>(loadEquipFromResultSet(rs), cid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsCommon(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
|
||||
if (login) {
|
||||
query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
|
||||
} else {
|
||||
int petid = rs.getInt("petid");
|
||||
if (rs.wasNull()) {
|
||||
petid = -1;
|
||||
}
|
||||
|
||||
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), petid);
|
||||
item.setOwner(rs.getString("owner"));
|
||||
item.setExpiration(rs.getLong("expiration"));
|
||||
item.setGiftFrom(rs.getString("giftFrom"));
|
||||
item.setFlag((short) rs.getInt("flag"));
|
||||
items.add(new Pair<>(item, mit));
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} finally {
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private void saveItemsCommon(List<Pair<Item, MapleInventoryType>> items, int id, Connection con) throws SQLException {
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement pse = null;
|
||||
ResultSet rs = null;
|
||||
|
||||
Lock lock = locks[id % lockCount];
|
||||
lock.lock();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
ps = con.prepareStatement("INSERT INTO `inventoryitems` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
for (Pair<Item, MapleInventoryType> pair : items) {
|
||||
Item item = pair.getLeft();
|
||||
MapleInventoryType mit = pair.getRight();
|
||||
ps.setInt(1, value);
|
||||
ps.setString(2, account ? null : String.valueOf(id));
|
||||
ps.setString(3, account ? String.valueOf(id) : null);
|
||||
ps.setInt(4, item.getItemId());
|
||||
ps.setInt(5, mit.getType());
|
||||
ps.setInt(6, item.getPosition());
|
||||
ps.setInt(7, item.getQuantity());
|
||||
ps.setString(8, item.getOwner());
|
||||
ps.setInt(9, item.getPetId()); // thanks Daddy Egg for alerting a case of unique petid constraint breach getting raised
|
||||
ps.setInt(10, item.getFlag());
|
||||
ps.setLong(11, item.getExpiration());
|
||||
ps.setString(12, item.getGiftFrom());
|
||||
ps.executeUpdate();
|
||||
|
||||
pse = con.prepareStatement("INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
rs = ps.getGeneratedKeys();
|
||||
|
||||
if (!rs.next()) {
|
||||
throw new RuntimeException("Inserting item failed.");
|
||||
}
|
||||
|
||||
pse.setInt(1, rs.getInt(1));
|
||||
rs.close();
|
||||
|
||||
Equip equip = (Equip) item;
|
||||
pse.setInt(2, equip.getUpgradeSlots());
|
||||
pse.setInt(3, equip.getLevel());
|
||||
pse.setInt(4, equip.getStr());
|
||||
pse.setInt(5, equip.getDex());
|
||||
pse.setInt(6, equip.getInt());
|
||||
pse.setInt(7, equip.getLuk());
|
||||
pse.setInt(8, equip.getHp());
|
||||
pse.setInt(9, equip.getMp());
|
||||
pse.setInt(10, equip.getWatk());
|
||||
pse.setInt(11, equip.getMatk());
|
||||
pse.setInt(12, equip.getWdef());
|
||||
pse.setInt(13, equip.getMdef());
|
||||
pse.setInt(14, equip.getAcc());
|
||||
pse.setInt(15, equip.getAvoid());
|
||||
pse.setInt(16, equip.getHands());
|
||||
pse.setInt(17, equip.getSpeed());
|
||||
pse.setInt(18, equip.getJump());
|
||||
pse.setInt(19, 0);
|
||||
pse.setInt(20, equip.getVicious());
|
||||
pse.setInt(21, equip.getItemLevel());
|
||||
pse.setInt(22, equip.getItemExp());
|
||||
pse.setInt(23, equip.getRingId());
|
||||
pse.executeUpdate();
|
||||
}
|
||||
|
||||
pse.close();
|
||||
}
|
||||
}
|
||||
|
||||
ps.close();
|
||||
} finally {
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (pse != null && !pse.isClosed()) {
|
||||
pse.close();
|
||||
}
|
||||
if(rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsMerchant(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null, ps2 = null;
|
||||
ResultSet rs = null, rs2 = null;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
|
||||
if (login) {
|
||||
query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
ps2 = con.prepareStatement("SELECT `bundles` FROM `inventorymerchant` WHERE `inventoryitemid` = ?");
|
||||
ps2.setInt(1, rs.getInt("inventoryitemid"));
|
||||
rs2 = ps2.executeQuery();
|
||||
|
||||
short bundles = 0;
|
||||
if(rs2.next()) {
|
||||
bundles = rs2.getShort("bundles");
|
||||
}
|
||||
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
|
||||
} else {
|
||||
if(bundles > 0) {
|
||||
int petid = rs.getInt("petid");
|
||||
if (rs.wasNull()) {
|
||||
petid = -1;
|
||||
}
|
||||
|
||||
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), petid);
|
||||
item.setOwner(rs.getString("owner"));
|
||||
item.setExpiration(rs.getLong("expiration"));
|
||||
item.setGiftFrom(rs.getString("giftFrom"));
|
||||
item.setFlag((short) rs.getInt("flag"));
|
||||
items.add(new Pair<>(item, mit));
|
||||
}
|
||||
}
|
||||
|
||||
rs2.close();
|
||||
ps2.close();
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} finally {
|
||||
if (rs2 != null && !rs2.isClosed()) {
|
||||
rs2.close();
|
||||
}
|
||||
if (ps2 != null && !ps2.isClosed()) {
|
||||
ps2.close();
|
||||
}
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private void saveItemsMerchant(List<Pair<Item, MapleInventoryType>> items, List<Short> bundlesList, int id, Connection con) throws SQLException {
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement pse = null;
|
||||
ResultSet rs = null;
|
||||
|
||||
Lock lock = locks[id % lockCount];
|
||||
lock.lock();
|
||||
try {
|
||||
ps = con.prepareStatement("DELETE FROM `inventorymerchant` WHERE `characterid` = ?");
|
||||
ps.setInt(1, id);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("DELETE `inventoryitems`, `inventoryequipment` FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
ps = con.prepareStatement("INSERT INTO `inventoryitems` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
|
||||
if (!items.isEmpty()) {
|
||||
int i = 0;
|
||||
for (Pair<Item, MapleInventoryType> pair : items) {
|
||||
Item item = pair.getLeft();
|
||||
Short bundles = bundlesList.get(i);
|
||||
MapleInventoryType mit = pair.getRight();
|
||||
i++;
|
||||
|
||||
ps.setInt(1, value);
|
||||
ps.setString(2, account ? null : String.valueOf(id));
|
||||
ps.setString(3, account ? String.valueOf(id) : null);
|
||||
ps.setInt(4, item.getItemId());
|
||||
ps.setInt(5, mit.getType());
|
||||
ps.setInt(6, item.getPosition());
|
||||
ps.setInt(7, item.getQuantity());
|
||||
ps.setString(8, item.getOwner());
|
||||
ps.setInt(9, item.getPetId());
|
||||
ps.setInt(10, item.getFlag());
|
||||
ps.setLong(11, item.getExpiration());
|
||||
ps.setString(12, item.getGiftFrom());
|
||||
ps.executeUpdate();
|
||||
|
||||
rs = ps.getGeneratedKeys();
|
||||
if (!rs.next()) {
|
||||
throw new RuntimeException("Inserting item failed.");
|
||||
}
|
||||
|
||||
int genKey = rs.getInt(1);
|
||||
rs.close();
|
||||
|
||||
pse = con.prepareStatement("INSERT INTO `inventorymerchant` VALUES (DEFAULT, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
|
||||
pse.setInt(1, genKey);
|
||||
pse.setInt(2, id);
|
||||
pse.setInt(3, bundles);
|
||||
pse.executeUpdate();
|
||||
pse.close();
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
pse = con.prepareStatement("INSERT INTO `inventoryequipment` VALUES (DEFAULT, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
pse.setInt(1, genKey);
|
||||
|
||||
Equip equip = (Equip) item;
|
||||
pse.setInt(2, equip.getUpgradeSlots());
|
||||
pse.setInt(3, equip.getLevel());
|
||||
pse.setInt(4, equip.getStr());
|
||||
pse.setInt(5, equip.getDex());
|
||||
pse.setInt(6, equip.getInt());
|
||||
pse.setInt(7, equip.getLuk());
|
||||
pse.setInt(8, equip.getHp());
|
||||
pse.setInt(9, equip.getMp());
|
||||
pse.setInt(10, equip.getWatk());
|
||||
pse.setInt(11, equip.getMatk());
|
||||
pse.setInt(12, equip.getWdef());
|
||||
pse.setInt(13, equip.getMdef());
|
||||
pse.setInt(14, equip.getAcc());
|
||||
pse.setInt(15, equip.getAvoid());
|
||||
pse.setInt(16, equip.getHands());
|
||||
pse.setInt(17, equip.getSpeed());
|
||||
pse.setInt(18, equip.getJump());
|
||||
pse.setInt(19, 0);
|
||||
pse.setInt(20, equip.getVicious());
|
||||
pse.setInt(21, equip.getItemLevel());
|
||||
pse.setInt(22, equip.getItemExp());
|
||||
pse.setInt(23, equip.getRingId());
|
||||
pse.executeUpdate();
|
||||
|
||||
pse.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ps.close();
|
||||
} finally {
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
if (pse != null && !pse.isClosed()) {
|
||||
pse.close();
|
||||
}
|
||||
if(rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
677
src/main/java/client/inventory/MapleInventory.java
Normal file
677
src/main/java/client/inventory/MapleInventory.java
Normal file
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
|
||||
|
||||
import tools.Pair;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import constants.inventory.ItemConstants;
|
||||
import server.MapleItemInformationProvider;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import tools.FilePrinter;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import server.ThreadManager;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze, Ronan
|
||||
*/
|
||||
public class MapleInventory implements Iterable<Item> {
|
||||
protected MapleCharacter owner;
|
||||
protected Map<Short, Item> inventory = new LinkedHashMap<>();
|
||||
protected byte slotLimit;
|
||||
protected MapleInventoryType type;
|
||||
protected boolean checked = false;
|
||||
protected Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.INVENTORY, true);
|
||||
|
||||
public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) {
|
||||
this.owner = mc;
|
||||
this.inventory = new LinkedHashMap<>();
|
||||
this.type = type;
|
||||
this.slotLimit = slotLimit;
|
||||
}
|
||||
|
||||
public boolean isExtendableInventory() { // not sure about cash, basing this on the previous one.
|
||||
return !(type.equals(MapleInventoryType.UNDEFINED) || type.equals(MapleInventoryType.EQUIPPED) || type.equals(MapleInventoryType.CASH));
|
||||
}
|
||||
|
||||
public boolean isEquipInventory() {
|
||||
return type.equals(MapleInventoryType.EQUIP) || type.equals(MapleInventoryType.EQUIPPED);
|
||||
}
|
||||
|
||||
public byte getSlotLimit() {
|
||||
lock.lock();
|
||||
try {
|
||||
return slotLimit;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSlotLimit(int newLimit) {
|
||||
lock.lock();
|
||||
try {
|
||||
if (newLimit < slotLimit) {
|
||||
List<Short> toRemove = new LinkedList<>();
|
||||
for (Item it : list()) {
|
||||
if (it.getPosition() > newLimit) {
|
||||
toRemove.add(it.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
for (Short slot : toRemove) {
|
||||
removeSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
slotLimit = (byte) newLimit;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Item> list() {
|
||||
lock.lock();
|
||||
try {
|
||||
return Collections.unmodifiableCollection(inventory.values());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Item findById(int itemId) {
|
||||
for (Item item : list()) {
|
||||
if (item.getItemId() == itemId) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Item findByName(String name) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
for (Item item : list()) {
|
||||
String itemName = ii.getName(item.getItemId());
|
||||
if(itemName == null) {
|
||||
FilePrinter.printError(FilePrinter.EXCEPTION, "[CRITICAL] Item " + item.getItemId() + " has no name.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.compareToIgnoreCase(itemName) == 0) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int countById(int itemId) {
|
||||
int qty = 0;
|
||||
for (Item item : list()) {
|
||||
if (item.getItemId() == itemId) {
|
||||
qty += item.getQuantity();
|
||||
}
|
||||
}
|
||||
return qty;
|
||||
}
|
||||
|
||||
public int countNotOwnedById(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;
|
||||
|
||||
if(!ItemConstants.isRechargeable(itemId)) {
|
||||
for (Item item : itemList) {
|
||||
required -= item.getQuantity();
|
||||
|
||||
if(required >= 0) {
|
||||
openSlot++;
|
||||
if(required == 0) return openSlot;
|
||||
} else {
|
||||
return openSlot;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Item item : itemList) {
|
||||
required -= 1;
|
||||
|
||||
if(required >= 0) {
|
||||
openSlot++;
|
||||
if(required == 0) return openSlot;
|
||||
} else {
|
||||
return openSlot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public List<Item> listById(int itemId) {
|
||||
List<Item> ret = new ArrayList<>();
|
||||
for (Item item : list()) {
|
||||
if (item.getItemId() == itemId) {
|
||||
ret.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.size() > 1) {
|
||||
Collections.sort(ret, new Comparator<Item>() {
|
||||
@Override
|
||||
public int compare(Item i1, Item i2) {
|
||||
return i1.getPosition() - i2.getPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public List<Item> linkedListById(int itemId) {
|
||||
List<Item> ret = new LinkedList<>();
|
||||
for (Item item : list()) {
|
||||
if (item.getItemId() == itemId) {
|
||||
ret.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret.size() > 1) {
|
||||
Collections.sort(ret, new Comparator<Item>() {
|
||||
@Override
|
||||
public int compare(Item i1, Item i2) {
|
||||
return i1.getPosition() - i2.getPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public short addItem(Item item) {
|
||||
short slotId = addSlot(item);
|
||||
if (slotId == -1) {
|
||||
return -1;
|
||||
}
|
||||
item.setPosition(slotId);
|
||||
return slotId;
|
||||
}
|
||||
|
||||
public void addItemFromDB(Item item) {
|
||||
if (item.getPosition() < 0 && !type.equals(MapleInventoryType.EQUIPPED)) {
|
||||
return;
|
||||
}
|
||||
addSlotFromDB(item.getPosition(), item);
|
||||
}
|
||||
|
||||
private static boolean isSameOwner(Item source, Item target) {
|
||||
return source.getOwner().equals(target.getOwner());
|
||||
}
|
||||
|
||||
public void move(short sSlot, short dSlot, short slotMax) {
|
||||
lock.lock();
|
||||
try {
|
||||
Item source = (Item) inventory.get(sSlot);
|
||||
Item target = (Item) inventory.get(dSlot);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
if (target == null) {
|
||||
source.setPosition(dSlot);
|
||||
inventory.put(dSlot, source);
|
||||
inventory.remove(sSlot);
|
||||
} else if (target.getItemId() == source.getItemId() && !ItemConstants.isRechargeable(source.getItemId()) && isSameOwner(source, target)) {
|
||||
if (type.getType() == MapleInventoryType.EQUIP.getType() || type.getType() == MapleInventoryType.CASH.getType()) {
|
||||
swap(target, source);
|
||||
} else if (source.getQuantity() + target.getQuantity() > slotMax) {
|
||||
short rest = (short) ((source.getQuantity() + target.getQuantity()) - slotMax);
|
||||
source.setQuantity(rest);
|
||||
target.setQuantity(slotMax);
|
||||
} else {
|
||||
target.setQuantity((short) (source.getQuantity() + target.getQuantity()));
|
||||
inventory.remove(sSlot);
|
||||
}
|
||||
} else {
|
||||
swap(target, source);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void swap(Item source, Item target) {
|
||||
inventory.remove(source.getPosition());
|
||||
inventory.remove(target.getPosition());
|
||||
short swapPos = source.getPosition();
|
||||
source.setPosition(target.getPosition());
|
||||
target.setPosition(swapPos);
|
||||
inventory.put(source.getPosition(), source);
|
||||
inventory.put(target.getPosition(), target);
|
||||
}
|
||||
|
||||
public Item getItem(short slot) {
|
||||
lock.lock();
|
||||
try {
|
||||
return inventory.get(slot);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeItem(short slot) {
|
||||
removeItem(slot, (short) 1, false);
|
||||
}
|
||||
|
||||
public void removeItem(short slot, short quantity, boolean allowZero) {
|
||||
Item item = getItem(slot);
|
||||
if (item == null) {// TODO is it ok not to throw an exception here?
|
||||
return;
|
||||
}
|
||||
item.setQuantity((short) (item.getQuantity() - quantity));
|
||||
if (item.getQuantity() < 0) {
|
||||
item.setQuantity((short) 0);
|
||||
}
|
||||
if (item.getQuantity() == 0 && !allowZero) {
|
||||
removeSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
protected short addSlot(Item item) {
|
||||
if(item == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
short slotId;
|
||||
lock.lock();
|
||||
try {
|
||||
slotId = getNextFreeSlot();
|
||||
if (slotId < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
inventory.put(slotId, item);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if (ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
ThreadManager.getInstance().newTask(new Runnable() { // deadlocks with coupons rates found thanks to GabrielSin & Masterrulax
|
||||
@Override
|
||||
public void run() {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return slotId;
|
||||
}
|
||||
|
||||
protected void addSlotFromDB(short slot, Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.put(slot, item);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if (ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
ThreadManager.getInstance().newTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void removeSlot(short slot) {
|
||||
Item item;
|
||||
lock.lock();
|
||||
try {
|
||||
item = inventory.remove(slot);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
if (item != null && ItemConstants.isRateCoupon(item.getItemId())) {
|
||||
ThreadManager.getInstance().newTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
owner.updateCouponRates();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFull() {
|
||||
lock.lock();
|
||||
try {
|
||||
return inventory.size() >= slotLimit;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isFull(int margin) {
|
||||
lock.lock();
|
||||
try {
|
||||
//System.out.print("(" + inventory.size() + " " + margin + " <> " + slotLimit + ")");
|
||||
return inventory.size() + margin >= slotLimit;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public short getNextFreeSlot() {
|
||||
if (isFull()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
for (short i = 1; i <= slotLimit; i++) {
|
||||
if (!inventory.containsKey(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public short getNumFreeSlot() {
|
||||
if (isFull()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
short free = 0;
|
||||
for (short i = 1; i <= slotLimit; i++) {
|
||||
if (!inventory.containsKey(i)) {
|
||||
free++;
|
||||
}
|
||||
}
|
||||
return free;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean checkItemRestricted(List<Pair<Item, MapleInventoryType>> items) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
// thanks Shavit for noticing set creation that would be only effective in rare situations
|
||||
for (Pair<Item, MapleInventoryType> p : items) {
|
||||
int itemid = p.getLeft().getItemId();
|
||||
if (ii.isPickupRestricted(itemid) && p.getLeft().getQuantity() > 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkSpot(MapleCharacter chr, Item item) { // thanks Vcoc for noticing pshops not checking item stacks when taking item back
|
||||
return checkSpot(chr, Collections.singletonList(item));
|
||||
}
|
||||
|
||||
public static boolean checkSpot(MapleCharacter chr, List<Item> items) {
|
||||
List<Pair<Item, MapleInventoryType>> listItems = new LinkedList<>();
|
||||
for (Item item : items) {
|
||||
listItems.add(new Pair<>(item, item.getInventoryType()));
|
||||
}
|
||||
|
||||
return checkSpotsAndOwnership(chr, listItems);
|
||||
}
|
||||
|
||||
public static boolean checkSpots(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items) {
|
||||
return checkSpots(chr, items, false);
|
||||
}
|
||||
|
||||
public static boolean checkSpots(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, boolean useProofInv) {
|
||||
int invTypesSize = MapleInventoryType.values().length;
|
||||
List<Integer> zeroedList = new ArrayList<>(invTypesSize);
|
||||
for(byte i = 0; i < invTypesSize; i++) zeroedList.add(0);
|
||||
|
||||
return checkSpots(chr, items, zeroedList, useProofInv);
|
||||
}
|
||||
|
||||
public static boolean checkSpots(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, List<Integer> typesSlotsUsed, boolean useProofInv) {
|
||||
// assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0.
|
||||
|
||||
if (!checkItemRestricted(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> rcvItems = new LinkedHashMap<>();
|
||||
Map<Integer, Byte> rcvTypes = new LinkedHashMap<>();
|
||||
|
||||
for (Pair<Item, MapleInventoryType> item : items) {
|
||||
Integer itemId = item.left.getItemId();
|
||||
List<Integer> qty = rcvItems.get(itemId);
|
||||
|
||||
if(qty == null) {
|
||||
List<Integer> itemQtyList = new LinkedList<>();
|
||||
itemQtyList.add((int) item.left.getQuantity());
|
||||
|
||||
rcvItems.put(itemId, itemQtyList);
|
||||
rcvTypes.put(itemId, item.right.getType());
|
||||
} else {
|
||||
if (!ItemConstants.isEquipment(itemId) && !ItemConstants.isRechargeable(itemId)) {
|
||||
qty.set(0, qty.get(0) + item.left.getQuantity());
|
||||
} else {
|
||||
qty.add((int) item.left.getQuantity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapleClient c = chr.getClient();
|
||||
for(Entry<Integer, List<Integer>> it: rcvItems.entrySet()) {
|
||||
int itemType = rcvTypes.get(it.getKey()) - 1;
|
||||
|
||||
for (Integer itValue : it.getValue()) {
|
||||
int usedSlots = typesSlotsUsed.get(itemType);
|
||||
|
||||
int result = MapleInventoryManipulator.checkSpaceProgressively(c, it.getKey(), itValue, "", usedSlots, useProofInv);
|
||||
boolean hasSpace = ((result % 2) != 0);
|
||||
|
||||
if(!hasSpace) return false;
|
||||
typesSlotsUsed.set(itemType, (result >> 1));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static long fnvHash32(final String k) {
|
||||
final int FNV_32_INIT = 0x811c9dc5;
|
||||
final int FNV_32_PRIME = 0x01000193;
|
||||
|
||||
int rv = FNV_32_INIT;
|
||||
final int len = k.length();
|
||||
for(int i = 0; i < len; i++) {
|
||||
rv ^= k.charAt(i);
|
||||
rv *= FNV_32_PRIME;
|
||||
}
|
||||
|
||||
return rv >= 0 ? rv : (2L * Integer.MAX_VALUE) + rv;
|
||||
}
|
||||
|
||||
private static Long hashKey(Integer itemId, String owner) {
|
||||
return (itemId.longValue() << 32L) + fnvHash32(owner);
|
||||
}
|
||||
|
||||
public static boolean checkSpotsAndOwnership(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items) {
|
||||
return checkSpotsAndOwnership(chr, items, false);
|
||||
}
|
||||
|
||||
public static boolean checkSpotsAndOwnership(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, boolean useProofInv) {
|
||||
List<Integer> zeroedList = new ArrayList<>(5);
|
||||
for(byte i = 0; i < 5; i++) zeroedList.add(0);
|
||||
|
||||
return checkSpotsAndOwnership(chr, items, zeroedList, useProofInv);
|
||||
}
|
||||
|
||||
public static boolean checkSpotsAndOwnership(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items, List<Integer> typesSlotsUsed, boolean useProofInv) {
|
||||
//assumption: no "UNDEFINED" or "EQUIPPED" items shall be tested here, all counts are >= 0 and item list to be checked is a legal one.
|
||||
|
||||
if (!checkItemRestricted(items)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Map<Long, List<Integer>> rcvItems = new LinkedHashMap<>();
|
||||
Map<Long, Byte> rcvTypes = new LinkedHashMap<>();
|
||||
Map<Long, String> rcvOwners = new LinkedHashMap<>();
|
||||
|
||||
for (Pair<Item, MapleInventoryType> item : items) {
|
||||
Long itemHash = hashKey(item.left.getItemId(), item.left.getOwner());
|
||||
List<Integer> qty = rcvItems.get(itemHash);
|
||||
|
||||
if(qty == null) {
|
||||
List<Integer> itemQtyList = new LinkedList<>();
|
||||
itemQtyList.add((int) item.left.getQuantity());
|
||||
|
||||
rcvItems.put(itemHash, itemQtyList);
|
||||
rcvTypes.put(itemHash, item.right.getType());
|
||||
rcvOwners.put(itemHash, item.left.getOwner());
|
||||
} else {
|
||||
// thanks BHB88 for pointing out an issue with rechargeable items being stacked on inventory check
|
||||
if (!ItemConstants.isEquipment(item.left.getItemId()) && !ItemConstants.isRechargeable(item.left.getItemId())) {
|
||||
qty.set(0, qty.get(0) + item.left.getQuantity());
|
||||
} else {
|
||||
qty.add((int) item.left.getQuantity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapleClient c = chr.getClient();
|
||||
for(Entry<Long, List<Integer>> it: rcvItems.entrySet()) {
|
||||
int itemType = rcvTypes.get(it.getKey()) - 1;
|
||||
int itemId = (int) (it.getKey() >> 32L);
|
||||
|
||||
for (Integer itValue : it.getValue()) {
|
||||
int usedSlots = typesSlotsUsed.get(itemType);
|
||||
|
||||
//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, itValue, rcvOwners.get(it.getKey()), usedSlots, useProofInv);
|
||||
boolean hasSpace = ((result % 2) != 0);
|
||||
//System.out.print(" -> hasSpace: " + hasSpace + " RESULT : " + result + "\n");
|
||||
|
||||
if(!hasSpace) return false;
|
||||
typesSlotsUsed.set(itemType, (result >> 1));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public MapleInventoryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Item> iterator() {
|
||||
return Collections.unmodifiableCollection(list()).iterator();
|
||||
}
|
||||
|
||||
public Collection<MapleInventory> allInventories() {
|
||||
return Collections.singletonList(this);
|
||||
}
|
||||
|
||||
public Item findByCashId(int cashId) {
|
||||
boolean isRing = false;
|
||||
Equip equip = null;
|
||||
for (Item item : list()) {
|
||||
if (item.getInventoryType().equals(MapleInventoryType.EQUIP)) {
|
||||
equip = (Equip) item;
|
||||
isRing = equip.getRingId() > -1;
|
||||
}
|
||||
if ((item.getPetId() > -1 ? item.getPetId() : isRing ? equip.getRingId() : item.getCashId()) == cashId)
|
||||
return item;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean checked() {
|
||||
lock.lock();
|
||||
try {
|
||||
return checked;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void checked(boolean yes) {
|
||||
lock.lock();
|
||||
try {
|
||||
checked = yes;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void lockInventory() {
|
||||
lock.lock();
|
||||
}
|
||||
|
||||
public void unlockInventory() {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
owner = null;
|
||||
}
|
||||
}
|
||||
99
src/main/java/client/inventory/MapleInventoryProof.java
Normal file
99
src/main/java/client/inventory/MapleInventoryProof.java
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.inventory;
|
||||
|
||||
import client.MapleCharacter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MapleInventoryProof extends MapleInventory {
|
||||
|
||||
public MapleInventoryProof(MapleCharacter mc) {
|
||||
super(mc, MapleInventoryType.CANHOLD, (byte) 0);
|
||||
}
|
||||
|
||||
public void cloneContents(MapleInventory inv) {
|
||||
inv.lockInventory();
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.clear();
|
||||
this.setSlotLimit(inv.getSlotLimit());
|
||||
|
||||
for(Item it : inv.list()) {
|
||||
Item item = new Item(it.getItemId(), it.getPosition(), it.getQuantity());
|
||||
inventory.put(item.getPosition(), item);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
public void flushContents() {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.clear();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected short addSlot(Item item) {
|
||||
if(item == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
try {
|
||||
short slotId = getNextFreeSlot();
|
||||
if (slotId < 0) {
|
||||
return -1;
|
||||
}
|
||||
inventory.put(slotId, item);
|
||||
|
||||
return slotId;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addSlotFromDB(short slot, Item item) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.put(slot, item);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSlot(short slot) {
|
||||
lock.lock();
|
||||
try {
|
||||
inventory.remove(slot);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/main/java/client/inventory/MapleInventoryType.java
Normal file
73
src/main/java/client/inventory/MapleInventoryType.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
*/
|
||||
public enum MapleInventoryType {
|
||||
UNDEFINED(0),
|
||||
EQUIP(1),
|
||||
USE(2),
|
||||
SETUP(3),
|
||||
ETC(4),
|
||||
CASH(5),
|
||||
CANHOLD(6), //Proof-guard for inserting after removal checks
|
||||
EQUIPPED(-1); //Seems nexon screwed something when removing an item T_T
|
||||
final byte type;
|
||||
|
||||
private MapleInventoryType(int type) {
|
||||
this.type = (byte) type;
|
||||
}
|
||||
|
||||
public byte getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public short getBitfieldEncoding() {
|
||||
return (short) (2 << type);
|
||||
}
|
||||
|
||||
public static MapleInventoryType getByType(byte type) {
|
||||
for (MapleInventoryType l : MapleInventoryType.values()) {
|
||||
if (l.getType() == type) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MapleInventoryType getByWZName(String name) {
|
||||
if (name.equals("Install")) {
|
||||
return SETUP;
|
||||
} else if (name.equals("Consume")) {
|
||||
return USE;
|
||||
} else if (name.equals("Etc")) {
|
||||
return ETC;
|
||||
} else if (name.equals("Cash")) {
|
||||
return CASH;
|
||||
} else if (name.equals("Pet")) {
|
||||
return CASH;
|
||||
}
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
330
src/main/java/client/inventory/MaplePet.java
Normal file
330
src/main/java/client/inventory/MaplePet.java
Normal file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
import constants.game.ExpTable;
|
||||
import java.awt.Point;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import tools.DatabaseConnection;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.movement.AbsoluteLifeMovement;
|
||||
import server.movement.LifeMovement;
|
||||
import server.movement.LifeMovementFragment;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.manipulator.MapleCashidGenerator;
|
||||
import java.sql.Connection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
*/
|
||||
public class MaplePet extends Item {
|
||||
private String name;
|
||||
private int uniqueid;
|
||||
private int closeness = 0;
|
||||
private byte level = 1;
|
||||
private int fullness = 100;
|
||||
private int Fh;
|
||||
private Point pos;
|
||||
private int stance;
|
||||
private boolean summoned;
|
||||
private int petFlag = 0;
|
||||
|
||||
public enum PetFlag {
|
||||
OWNER_SPEED(0x01);
|
||||
|
||||
private int i;
|
||||
|
||||
private PetFlag(int i) {
|
||||
this.i = i;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
private MaplePet(int id, short position, int uniqueid) {
|
||||
super(id, position, (short) 1);
|
||||
this.uniqueid = uniqueid;
|
||||
this.pos = new Point(0, 0);
|
||||
}
|
||||
|
||||
public static MaplePet loadFromDb(int itemid, short position, int petid) {
|
||||
try {
|
||||
MaplePet ret = new MaplePet(itemid, position, petid);
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT name, level, closeness, fullness, summoned, flag FROM pets WHERE petid = ?"); // Get pet details..
|
||||
ps.setInt(1, petid);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
rs.next();
|
||||
ret.setName(rs.getString("name"));
|
||||
ret.setCloseness(Math.min(rs.getInt("closeness"), 30000));
|
||||
ret.setLevel((byte) Math.min(rs.getByte("level"), 30));
|
||||
ret.setFullness(Math.min(rs.getInt("fullness"), 100));
|
||||
ret.setSummoned(rs.getInt("summoned") == 1);
|
||||
ret.setPetFlag(rs.getInt("flag"));
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteFromDb(MapleCharacter owner, int petid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion
|
||||
ps.setInt(1, petid);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
con.close();
|
||||
|
||||
owner.resetExcluded(petid);
|
||||
MapleCashidGenerator.freeCashId(petid);
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveToDb() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("UPDATE pets SET name = ?, level = ?, closeness = ?, fullness = ?, summoned = ?, flag = ? WHERE petid = ?");
|
||||
ps.setString(1, getName());
|
||||
ps.setInt(2, getLevel());
|
||||
ps.setInt(3, getCloseness());
|
||||
ps.setInt(4, getFullness());
|
||||
ps.setInt(5, isSummoned() ? 1 : 0);
|
||||
ps.setInt(6, getPetFlag());
|
||||
ps.setInt(7, getUniqueId());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static int createPet(int itemid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, 1, 0, 100, 0, 0)");
|
||||
int ret = MapleCashidGenerator.generateCashId();
|
||||
ps.setInt(1, ret);
|
||||
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static int createPet(int itemid, byte level, int closeness, int fullness) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned, flag) VALUES (?, ?, ?, ?, ?, 0, 0)");
|
||||
int ret = MapleCashidGenerator.generateCashId();
|
||||
ps.setInt(1, ret);
|
||||
ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
|
||||
ps.setByte(3, level);
|
||||
ps.setInt(4, closeness);
|
||||
ps.setInt(5, fullness);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
con.close();
|
||||
return ret;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getUniqueId() {
|
||||
return uniqueid;
|
||||
}
|
||||
|
||||
public void setUniqueId(int id) {
|
||||
this.uniqueid = id;
|
||||
}
|
||||
|
||||
public int getCloseness() {
|
||||
return closeness;
|
||||
}
|
||||
|
||||
public void setCloseness(int closeness) {
|
||||
this.closeness = closeness;
|
||||
}
|
||||
|
||||
public byte getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void gainClosenessFullness(MapleCharacter owner, int incCloseness, int incFullness, int type) {
|
||||
byte slot = owner.getPetIndex(this);
|
||||
boolean enjoyed;
|
||||
|
||||
//will NOT increase pet's closeness if tried to feed pet with 100% fullness
|
||||
if (fullness < 100 || incFullness == 0) { //incFullness == 0: command given
|
||||
int newFullness = fullness + incFullness;
|
||||
if (newFullness > 100) newFullness = 100;
|
||||
fullness = newFullness;
|
||||
|
||||
if (incCloseness > 0 && closeness < 30000) {
|
||||
int newCloseness = closeness + incCloseness;
|
||||
if (newCloseness > 30000) newCloseness = 30000;
|
||||
|
||||
closeness = newCloseness;
|
||||
while(newCloseness >= ExpTable.getClosenessNeededForLevel(level)) {
|
||||
level += 1;
|
||||
owner.getClient().announce(MaplePacketCreator.showOwnPetLevelUp(slot));
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.showPetLevelUp(owner, slot));
|
||||
}
|
||||
}
|
||||
|
||||
enjoyed = true;
|
||||
} else {
|
||||
int newCloseness = closeness - 1;
|
||||
if (newCloseness < 0) newCloseness = 0;
|
||||
|
||||
closeness = newCloseness;
|
||||
if (level > 1 && newCloseness < ExpTable.getClosenessNeededForLevel(level - 1)) {
|
||||
level -= 1;
|
||||
}
|
||||
|
||||
enjoyed = false;
|
||||
}
|
||||
|
||||
owner.getMap().broadcastMessage(MaplePacketCreator.petFoodResponse(owner.getId(), slot, enjoyed, false));
|
||||
saveToDb();
|
||||
|
||||
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
|
||||
if (petz != null)
|
||||
owner.forceUpdateItem(petz);
|
||||
}
|
||||
|
||||
public void setLevel(byte level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public int getFullness() {
|
||||
return fullness;
|
||||
}
|
||||
|
||||
public void setFullness(int fullness) {
|
||||
this.fullness = fullness;
|
||||
}
|
||||
|
||||
public int getFh() {
|
||||
return Fh;
|
||||
}
|
||||
|
||||
public void setFh(int Fh) {
|
||||
this.Fh = Fh;
|
||||
}
|
||||
|
||||
public Point getPos() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
public void setPos(Point pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public int getStance() {
|
||||
return stance;
|
||||
}
|
||||
|
||||
public void setStance(int stance) {
|
||||
this.stance = stance;
|
||||
}
|
||||
|
||||
public boolean isSummoned() {
|
||||
return summoned;
|
||||
}
|
||||
|
||||
public void setSummoned(boolean yes) {
|
||||
this.summoned = yes;
|
||||
}
|
||||
|
||||
public int getPetFlag() {
|
||||
return this.petFlag;
|
||||
}
|
||||
|
||||
private void setPetFlag(int flag) {
|
||||
this.petFlag = flag;
|
||||
}
|
||||
|
||||
public void addPetFlag(MapleCharacter owner, PetFlag flag) {
|
||||
this.petFlag |= flag.getValue();
|
||||
saveToDb();
|
||||
|
||||
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
|
||||
if (petz != null)
|
||||
owner.forceUpdateItem(petz);
|
||||
}
|
||||
|
||||
public void removePetFlag(MapleCharacter owner, PetFlag flag) {
|
||||
this.petFlag &= 0xFFFFFFFF ^ flag.getValue();
|
||||
saveToDb();
|
||||
|
||||
Item petz = owner.getInventory(MapleInventoryType.CASH).getItem(getPosition());
|
||||
if (petz != null)
|
||||
owner.forceUpdateItem(petz);
|
||||
}
|
||||
|
||||
public Pair<Integer, Boolean> canConsume(int itemId) {
|
||||
return MapleItemInformationProvider.getInstance().canPetConsume(this.getItemId(), itemId);
|
||||
}
|
||||
|
||||
public void updatePosition(List<LifeMovementFragment> movement) {
|
||||
for (LifeMovementFragment move : movement) {
|
||||
if (move instanceof LifeMovement) {
|
||||
if (move instanceof AbsoluteLifeMovement) {
|
||||
this.setPos(((LifeMovement) move).getPosition());
|
||||
}
|
||||
this.setStance(((LifeMovement) move).getNewstate());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/main/java/client/inventory/MapleWeaponType.java
Normal file
54
src/main/java/client/inventory/MapleWeaponType.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
public enum MapleWeaponType {
|
||||
NOT_A_WEAPON(0),
|
||||
GENERAL1H_SWING(4.4),
|
||||
GENERAL1H_STAB(3.2),
|
||||
GENERAL2H_SWING(4.8),
|
||||
GENERAL2H_STAB(3.4),
|
||||
BOW(3.4),
|
||||
CLAW(3.6),
|
||||
CROSSBOW(3.6),
|
||||
DAGGER_THIEVES(3.6),
|
||||
DAGGER_OTHER(4),
|
||||
GUN(3.6),
|
||||
KNUCKLE(4.8),
|
||||
POLE_ARM_SWING(5.0),
|
||||
POLE_ARM_STAB(3.0),
|
||||
SPEAR_STAB(5.0),
|
||||
SPEAR_SWING(3.0),
|
||||
STAFF(3.6),
|
||||
SWORD1H(4.0),
|
||||
SWORD2H(4.6),
|
||||
WAND(3.6);
|
||||
private double damageMultiplier;
|
||||
|
||||
private MapleWeaponType(double maxDamageMultiplier) {
|
||||
this.damageMultiplier = maxDamageMultiplier;
|
||||
}
|
||||
|
||||
public double getMaxDamageMultiplier() {
|
||||
return damageMultiplier;
|
||||
}
|
||||
}
|
||||
51
src/main/java/client/inventory/ModifyInventory.java
Normal file
51
src/main/java/client/inventory/ModifyInventory.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package client.inventory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author kevin
|
||||
*/
|
||||
public class ModifyInventory {
|
||||
|
||||
private int mode;
|
||||
private Item item;
|
||||
private short oldPos;
|
||||
|
||||
public ModifyInventory(final int mode, final Item item) {
|
||||
this.mode = mode;
|
||||
this.item = item.copy();
|
||||
}
|
||||
|
||||
public ModifyInventory(final int mode, final Item item, final short oldPos) {
|
||||
this.mode = mode;
|
||||
this.item = item.copy();
|
||||
this.oldPos = oldPos;
|
||||
}
|
||||
|
||||
public final int getMode() {
|
||||
return mode;
|
||||
}
|
||||
|
||||
public final int getInventoryType() {
|
||||
return item.getInventoryType().type;
|
||||
}
|
||||
|
||||
public final short getPosition() {
|
||||
return item.getPosition();
|
||||
}
|
||||
|
||||
public final short getOldPosition() {
|
||||
return oldPos;
|
||||
}
|
||||
|
||||
public final short getQuantity() {
|
||||
return item.getQuantity();
|
||||
}
|
||||
|
||||
public final Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public final void clear() {
|
||||
this.item = null;
|
||||
}
|
||||
}
|
||||
52
src/main/java/client/inventory/PetCommand.java
Normal file
52
src/main/java/client/inventory/PetCommand.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
/*
|
||||
* @author Leifde
|
||||
*/
|
||||
public class PetCommand {
|
||||
private int petId, skillId, prob, inc;
|
||||
|
||||
public PetCommand(int petId, int skillId, int prob, int inc) {
|
||||
this.petId = petId;
|
||||
this.skillId = skillId;
|
||||
this.prob = prob;
|
||||
this.inc = inc;
|
||||
}
|
||||
|
||||
public int getPetId() {
|
||||
return petId;
|
||||
}
|
||||
|
||||
public int getSkillId() {
|
||||
return skillId;
|
||||
}
|
||||
|
||||
public int getProbability() {
|
||||
return prob;
|
||||
}
|
||||
|
||||
public int getIncrease() {
|
||||
return inc;
|
||||
}
|
||||
}
|
||||
76
src/main/java/client/inventory/PetDataFactory.java
Normal file
76
src/main/java/client/inventory/PetDataFactory.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 client.inventory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import provider.MapleData;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import provider.MapleDataTool;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Danny (Leifde)
|
||||
*/
|
||||
public class PetDataFactory {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
private static Map<String, PetCommand> petCommands = new HashMap<String, PetCommand>();
|
||||
private static Map<Integer, Integer> petHunger = new HashMap<Integer, Integer>();
|
||||
|
||||
public static PetCommand getPetCommand(int petId, int skillId) {
|
||||
PetCommand ret = petCommands.get(Integer.valueOf(petId) + "" + skillId);
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
synchronized (petCommands) {
|
||||
ret = petCommands.get(petId + "" + skillId);
|
||||
if (ret == null) {
|
||||
MapleData skillData = dataRoot.getData("Pet/" + petId + ".img");
|
||||
int prob = 0;
|
||||
int inc = 0;
|
||||
if (skillData != null) {
|
||||
prob = MapleDataTool.getInt("interact/" + skillId + "/prob", skillData, 0);
|
||||
inc = MapleDataTool.getInt("interact/" + skillId + "/inc", skillData, 0);
|
||||
}
|
||||
ret = new PetCommand(petId, skillId, prob, inc);
|
||||
petCommands.put(petId + "" + skillId, ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getHunger(int petId) {
|
||||
Integer ret = petHunger.get(Integer.valueOf(petId));
|
||||
if (ret != null) {
|
||||
return ret;
|
||||
}
|
||||
synchronized (petHunger) {
|
||||
ret = petHunger.get(Integer.valueOf(petId));
|
||||
if (ret == null) {
|
||||
ret = Integer.valueOf(MapleDataTool.getInt(dataRoot.getData("Pet/" + petId + ".img").getChildByPath("info/hungry"), 1));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.inventory.manipulator;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MapleCashidGenerator {
|
||||
|
||||
private final static Set<Integer> existentCashids = new HashSet<>(10000);
|
||||
private static Integer runningCashid = 0;
|
||||
|
||||
private static void loadExistentCashIdsFromQuery(Connection con, String query) throws SQLException {
|
||||
PreparedStatement ps = con.prepareStatement(query);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
int id = rs.getInt(1);
|
||||
if (!rs.wasNull()) {
|
||||
existentCashids.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
}
|
||||
|
||||
public static synchronized void loadExistentCashIdsFromDb() {
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
|
||||
loadExistentCashIdsFromQuery(con, "SELECT id FROM rings");
|
||||
loadExistentCashIdsFromQuery(con, "SELECT petid FROM pets");
|
||||
|
||||
con.close();
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
runningCashid = 0;
|
||||
do {
|
||||
runningCashid++; // hopefully the id will never surpass the allotted amount for pets/rings?
|
||||
} while (existentCashids.contains(runningCashid));
|
||||
}
|
||||
|
||||
private static void getNextAvailableCashId() {
|
||||
runningCashid++;
|
||||
if (runningCashid >= 777000000) {
|
||||
existentCashids.clear();
|
||||
loadExistentCashIdsFromDb();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized int generateCashId() {
|
||||
while (true) {
|
||||
if (!existentCashids.contains(runningCashid)) {
|
||||
int ret = runningCashid;
|
||||
getNextAvailableCashId();
|
||||
|
||||
// existentCashids.add(ret)... no need to do this since the wrap over already refetches already used cashids from the DB
|
||||
return ret;
|
||||
}
|
||||
|
||||
getNextAvailableCashId();
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized void freeCashId(int cashId) {
|
||||
existentCashids.remove(cashId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
/*
|
||||
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 client.inventory.manipulator;
|
||||
|
||||
import client.MapleBuffStat;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import client.inventory.ModifyInventory;
|
||||
import client.newyear.NewYearCardRecord;
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.maps.MapleMap;
|
||||
import tools.FilePrinter;
|
||||
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan - improved check space feature and removed redundant object calls
|
||||
*/
|
||||
public class MapleInventoryManipulator {
|
||||
|
||||
public static boolean addById(MapleClient c, int itemId, short quantity) {
|
||||
return addById(c, itemId, quantity, null, -1, -1);
|
||||
}
|
||||
|
||||
public static boolean addById(MapleClient c, int itemId, short quantity, long expiration) {
|
||||
return addById(c, itemId, quantity, null, -1, (byte) 0, expiration);
|
||||
}
|
||||
|
||||
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid) {
|
||||
return addById(c, itemId, quantity, owner, petid, -1);
|
||||
}
|
||||
|
||||
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, long expiration) {
|
||||
return addById(c, itemId, quantity, owner, petid, (byte) 0, expiration);
|
||||
}
|
||||
|
||||
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, short flag, long expiration) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventoryType type = ItemConstants.getInventoryType(itemId);
|
||||
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, short flag, long expiration) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (!type.equals(MapleInventoryType.EQUIP)) {
|
||||
short slotMax = ii.getSlotMax(c, itemId);
|
||||
List<Item> existing = inv.listById(itemId);
|
||||
if (!ItemConstants.isRechargeable(itemId) && petid == -1) {
|
||||
if (existing.size() > 0) { // first update all existing slots to slotMax
|
||||
Iterator<Item> i = existing.iterator();
|
||||
while (quantity > 0) {
|
||||
if (i.hasNext()) {
|
||||
Item eItem = (Item) i.next();
|
||||
short oldQ = eItem.getQuantity();
|
||||
if (oldQ < slotMax && ((eItem.getOwner().equals(owner) || owner == null) && eItem.getFlag() == flag)) {
|
||||
short newQ = (short) Math.min(oldQ + quantity, slotMax);
|
||||
quantity -= (newQ - oldQ);
|
||||
eItem.setQuantity(newQ);
|
||||
eItem.setExpiration(expiration);
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, eItem))));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean sandboxItem = (flag & ItemConstants.SANDBOX) == ItemConstants.SANDBOX;
|
||||
while (quantity > 0) {
|
||||
short newQ = (short) Math.min(quantity, slotMax);
|
||||
if (newQ != 0) {
|
||||
quantity -= newQ;
|
||||
Item nItem = new Item(itemId, (short) 0, newQ, petid);
|
||||
nItem.setFlag(flag);
|
||||
nItem.setExpiration(expiration);
|
||||
short newSlot = inv.addItem(nItem);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return false;
|
||||
}
|
||||
if (owner != null) {
|
||||
nItem.setOwner(owner);
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
|
||||
if(sandboxItem) chr.setHasSandboxItem();
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Item nItem = new Item(itemId, (short) 0, quantity, petid);
|
||||
nItem.setFlag(flag);
|
||||
nItem.setExpiration(expiration);
|
||||
short newSlot = inv.addItem(nItem);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return false;
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
|
||||
if(MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
|
||||
}
|
||||
} else if (quantity == 1) {
|
||||
Item nEquip = ii.getEquipById(itemId);
|
||||
nEquip.setFlag(flag);
|
||||
nEquip.setExpiration(expiration);
|
||||
if (owner != null) {
|
||||
nEquip.setOwner(owner);
|
||||
}
|
||||
short newSlot = inv.addItem(nEquip);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return false;
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nEquip))));
|
||||
if(MapleInventoryManipulator.isSandboxItem(nEquip)) chr.setHasSandboxItem();
|
||||
} else {
|
||||
throw new RuntimeException("Trying to create equip with non-one quantity");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean addFromDrop(MapleClient c, Item item) {
|
||||
return addFromDrop(c, item, true);
|
||||
}
|
||||
|
||||
public static boolean addFromDrop(MapleClient c, Item item, boolean show) {
|
||||
return addFromDrop(c, item, show, item.getPetId());
|
||||
}
|
||||
|
||||
public static boolean addFromDrop(MapleClient c, Item item, boolean show, int petId) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventoryType type = item.getInventoryType();
|
||||
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
inv.lockInventory();
|
||||
try {
|
||||
return addFromDropInternal(c, chr, type, inv, item, show, petId);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean addFromDropInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, Item item, boolean show, int petId) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
int itemid = item.getItemId();
|
||||
if (ii.isPickupRestricted(itemid) && chr.haveItemWithId(itemid, true)) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.showItemUnavailable());
|
||||
return false;
|
||||
}
|
||||
short quantity = item.getQuantity();
|
||||
|
||||
if (!type.equals(MapleInventoryType.EQUIP)) {
|
||||
short slotMax = ii.getSlotMax(c, itemid);
|
||||
List<Item> existing = inv.listById(itemid);
|
||||
if (!ItemConstants.isRechargeable(itemid) && petId == -1) {
|
||||
if (existing.size() > 0) { // first update all existing slots to slotMax
|
||||
Iterator<Item> i = existing.iterator();
|
||||
while (quantity > 0) {
|
||||
if (i.hasNext()) {
|
||||
Item eItem = (Item) i.next();
|
||||
short oldQ = eItem.getQuantity();
|
||||
if (oldQ < slotMax && item.getFlag() == eItem.getFlag() && item.getOwner().equals(eItem.getOwner())) {
|
||||
short newQ = (short) Math.min(oldQ + quantity, slotMax);
|
||||
quantity -= (newQ - oldQ);
|
||||
eItem.setQuantity(newQ);
|
||||
item.setPosition(eItem.getPosition());
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, eItem))));
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (quantity > 0) {
|
||||
short newQ = (short) Math.min(quantity, slotMax);
|
||||
quantity -= newQ;
|
||||
Item nItem = new Item(itemid, (short) 0, newQ, petId);
|
||||
nItem.setExpiration(item.getExpiration());
|
||||
nItem.setOwner(item.getOwner());
|
||||
nItem.setFlag(item.getFlag());
|
||||
short newSlot = inv.addItem(nItem);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
item.setQuantity((short) (quantity + newQ));
|
||||
return false;
|
||||
}
|
||||
nItem.setPosition(newSlot);
|
||||
item.setPosition(newSlot);
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
|
||||
if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
|
||||
}
|
||||
} else {
|
||||
Item nItem = new Item(itemid, (short) 0, quantity, petId);
|
||||
nItem.setExpiration(item.getExpiration());
|
||||
nItem.setFlag(item.getFlag());
|
||||
|
||||
short newSlot = inv.addItem(nItem);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return false;
|
||||
}
|
||||
nItem.setPosition(newSlot);
|
||||
item.setPosition(newSlot);
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, nItem))));
|
||||
if (MapleInventoryManipulator.isSandboxItem(nItem)) chr.setHasSandboxItem();
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
} else if (quantity == 1) {
|
||||
short newSlot = inv.addItem(item);
|
||||
if (newSlot == -1) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return false;
|
||||
}
|
||||
item.setPosition(newSlot);
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(0, item))));
|
||||
if (MapleInventoryManipulator.isSandboxItem(item)) chr.setHasSandboxItem();
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.ITEM, "Tried to pickup Equip id " + itemid + " containing more than 1 quantity --> " + quantity);
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.showItemUnavailable());
|
||||
return false;
|
||||
}
|
||||
if (show) {
|
||||
c.announce(MaplePacketCreator.getShowItemGain(itemid, item.getQuantity()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean haveItemWithId(MapleInventory inv, int itemid) {
|
||||
return inv.findById(itemid) != null;
|
||||
}
|
||||
|
||||
public static boolean checkSpace(MapleClient c, int itemid, int quantity, String owner) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleInventoryType type = ItemConstants.getInventoryType(itemid);
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
|
||||
if (ii.isPickupRestricted(itemid)) {
|
||||
if (haveItemWithId(inv, itemid)) {
|
||||
return false;
|
||||
} else if (ItemConstants.isEquipment(itemid) && haveItemWithId(chr.getInventory(MapleInventoryType.EQUIPPED), itemid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!type.equals(MapleInventoryType.EQUIP)) {
|
||||
short slotMax = ii.getSlotMax(c, itemid);
|
||||
List<Item> existing = inv.listById(itemid);
|
||||
|
||||
final int numSlotsNeeded;
|
||||
if (ItemConstants.isRechargeable(itemid)) {
|
||||
numSlotsNeeded = 1;
|
||||
} else {
|
||||
if (existing.size() > 0) // first update all existing slots to slotMax
|
||||
{
|
||||
for (Item eItem : existing) {
|
||||
short oldQ = eItem.getQuantity();
|
||||
if (oldQ < slotMax && owner.equals(eItem.getOwner())) {
|
||||
short newQ = (short) Math.min(oldQ + quantity, slotMax);
|
||||
quantity -= (newQ - oldQ);
|
||||
}
|
||||
if (quantity <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slotMax > 0) {
|
||||
numSlotsNeeded = (int) (Math.ceil(((double) quantity) / slotMax));
|
||||
} else {
|
||||
numSlotsNeeded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return !inv.isFull(numSlotsNeeded - 1);
|
||||
} else {
|
||||
return !inv.isFull();
|
||||
}
|
||||
}
|
||||
|
||||
public static int checkSpaceProgressively(MapleClient c, int itemid, int quantity, String owner, int usedSlots, boolean useProofInv) {
|
||||
// return value --> bit0: if has space for this one;
|
||||
// value after: new slots filled;
|
||||
// assumption: equipments always have slotMax == 1.
|
||||
|
||||
int returnValue;
|
||||
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleInventoryType type = !useProofInv ? ItemConstants.getInventoryType(itemid) : MapleInventoryType.CANHOLD;
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
|
||||
if (ii.isPickupRestricted(itemid)) {
|
||||
if (haveItemWithId(inv, itemid)) {
|
||||
return 0;
|
||||
} else if (ItemConstants.isEquipment(itemid) && haveItemWithId(chr.getInventory(MapleInventoryType.EQUIPPED), itemid)) {
|
||||
return 0; // thanks Captain & Aika & Vcoc for pointing out inventory checkup on player trades missing out one-of-a-kind items.
|
||||
}
|
||||
}
|
||||
|
||||
if (!type.equals(MapleInventoryType.EQUIP)) {
|
||||
short slotMax = ii.getSlotMax(c, itemid);
|
||||
final int numSlotsNeeded;
|
||||
|
||||
if (ItemConstants.isRechargeable(itemid)) {
|
||||
numSlotsNeeded = 1;
|
||||
} else {
|
||||
List<Item> existing = inv.listById(itemid);
|
||||
|
||||
if (existing.size() > 0) // first update all existing slots to slotMax
|
||||
{
|
||||
for (Item eItem : existing) {
|
||||
short oldQ = eItem.getQuantity();
|
||||
if (oldQ < slotMax && owner.equals(eItem.getOwner())) {
|
||||
short newQ = (short) Math.min(oldQ + quantity, slotMax);
|
||||
quantity -= (newQ - oldQ);
|
||||
}
|
||||
if (quantity <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (slotMax > 0) {
|
||||
numSlotsNeeded = (int) (Math.ceil(((double) quantity) / slotMax));
|
||||
} else {
|
||||
numSlotsNeeded = 1;
|
||||
}
|
||||
}
|
||||
|
||||
returnValue = ((numSlotsNeeded + usedSlots) << 1);
|
||||
returnValue += (numSlotsNeeded == 0 || !inv.isFullAfterSomeItems(numSlotsNeeded - 1, usedSlots)) ? 1 : 0;
|
||||
//System.out.print(" needed " + numSlotsNeeded + " used " + usedSlots + " rval " + returnValue);
|
||||
} else {
|
||||
returnValue = ((quantity + usedSlots) << 1);
|
||||
returnValue += (!inv.isFullAfterSomeItems(0, usedSlots)) ? 1 : 0;
|
||||
//System.out.print(" eqpneeded " + 1 + " used " + usedSlots + " rval " + returnValue);
|
||||
}
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public static void removeFromSlot(MapleClient c, MapleInventoryType type, short slot, short quantity, boolean fromDrop) {
|
||||
removeFromSlot(c, type, slot, quantity, fromDrop, false);
|
||||
}
|
||||
|
||||
public static void removeFromSlot(MapleClient c, MapleInventoryType type, short slot, short quantity, boolean fromDrop, boolean consume) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
Item item = inv.getItem(slot);
|
||||
boolean allowZero = consume && ItemConstants.isRechargeable(item.getItemId());
|
||||
|
||||
if(type == MapleInventoryType.EQUIPPED) {
|
||||
inv.lockInventory();
|
||||
try {
|
||||
chr.unequippedItem((Equip) item);
|
||||
inv.removeItem(slot, quantity, allowZero);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
announceModifyInventory(c, item, fromDrop, allowZero);
|
||||
} else {
|
||||
int petid = item.getPetId();
|
||||
if (petid > -1) { // thanks Vcoc for finding a d/c issue with equipped pets and pets remaining on DB here
|
||||
int petIdx = chr.getPetIndex(petid);
|
||||
if(petIdx > -1) {
|
||||
MaplePet pet = chr.getPet(petIdx);
|
||||
chr.unequipPet(pet, true);
|
||||
}
|
||||
|
||||
inv.removeItem(slot, quantity, allowZero);
|
||||
if(type != MapleInventoryType.CANHOLD) {
|
||||
announceModifyInventory(c, item, fromDrop, allowZero);
|
||||
}
|
||||
|
||||
// thanks Robin Schulz for noticing pet issues when moving pets out of inventory
|
||||
} else {
|
||||
inv.removeItem(slot, quantity, allowZero);
|
||||
if(type != MapleInventoryType.CANHOLD) {
|
||||
announceModifyInventory(c, item, fromDrop, allowZero);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void announceModifyInventory(MapleClient c, Item item, boolean fromDrop, boolean allowZero) {
|
||||
if (item.getQuantity() == 0 && !allowZero) {
|
||||
c.announce(MaplePacketCreator.modifyInventory(fromDrop, Collections.singletonList(new ModifyInventory(3, item))));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.modifyInventory(fromDrop, Collections.singletonList(new ModifyInventory(1, item))));
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeById(MapleClient c, MapleInventoryType type, int itemId, int quantity, boolean fromDrop, boolean consume) {
|
||||
int removeQuantity = quantity;
|
||||
MapleInventory inv = c.getPlayer().getInventory(type);
|
||||
int slotLimit = type == MapleInventoryType.EQUIPPED ? 128 : inv.getSlotLimit();
|
||||
|
||||
for (short i = 0; i <= slotLimit; i++) {
|
||||
Item item = inv.getItem((short) (type == MapleInventoryType.EQUIPPED ? -i : i));
|
||||
if (item != null) {
|
||||
if (item.getItemId() == itemId || item.getCashId() == itemId) {
|
||||
if (removeQuantity <= item.getQuantity()) {
|
||||
removeFromSlot(c, type, item.getPosition(), (short) removeQuantity, fromDrop, consume);
|
||||
removeQuantity = 0;
|
||||
break;
|
||||
} else {
|
||||
removeQuantity -= item.getQuantity();
|
||||
removeFromSlot(c, type, item.getPosition(), item.getQuantity(), fromDrop, consume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removeQuantity > 0 && type != MapleInventoryType.CANHOLD) {
|
||||
throw new RuntimeException("[Hack] Not enough items available of Item:" + itemId + ", Quantity (After Quantity/Over Current Quantity): " + (quantity - removeQuantity) + "/" + quantity);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isSameOwner(Item source, Item target) {
|
||||
return source.getOwner().equals(target.getOwner());
|
||||
}
|
||||
|
||||
public static void move(MapleClient c, MapleInventoryType type, short src, short dst) {
|
||||
MapleInventory inv = c.getPlayer().getInventory(type);
|
||||
|
||||
if (src < 0 || dst < 0) {
|
||||
return;
|
||||
}
|
||||
if(dst > inv.getSlotLimit()) {
|
||||
return;
|
||||
}
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
Item source = inv.getItem(src);
|
||||
Item initialTarget = inv.getItem(dst);
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
short olddstQ = -1;
|
||||
if (initialTarget != null) {
|
||||
olddstQ = initialTarget.getQuantity();
|
||||
}
|
||||
short oldsrcQ = source.getQuantity();
|
||||
short slotMax = ii.getSlotMax(c, source.getItemId());
|
||||
inv.move(src, dst, slotMax);
|
||||
final List<ModifyInventory> mods = new ArrayList<>();
|
||||
if (!(type.equals(MapleInventoryType.EQUIP) || type.equals(MapleInventoryType.CASH)) && initialTarget != null && initialTarget.getItemId() == source.getItemId() && !ItemConstants.isRechargeable(source.getItemId()) && isSameOwner(source, initialTarget)) {
|
||||
if ((olddstQ + oldsrcQ) > slotMax) {
|
||||
mods.add(new ModifyInventory(1, source));
|
||||
mods.add(new ModifyInventory(1, initialTarget));
|
||||
} else {
|
||||
mods.add(new ModifyInventory(3, source));
|
||||
mods.add(new ModifyInventory(1, initialTarget));
|
||||
}
|
||||
} else {
|
||||
mods.add(new ModifyInventory(2, source, src));
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, mods));
|
||||
}
|
||||
|
||||
public static void equip(MapleClient c, short src, short dst) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory eqpInv = chr.getInventory(MapleInventoryType.EQUIP);
|
||||
MapleInventory eqpdInv = chr.getInventory(MapleInventoryType.EQUIPPED);
|
||||
|
||||
Equip source = (Equip) eqpInv.getItem(src);
|
||||
if (source == null || !ii.canWearEquipment(chr, source, dst)) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else if ((((source.getItemId() >= 1902000 && source.getItemId() <= 1902002) || source.getItemId() == 1912000) && chr.isCygnus()) || ((source.getItemId() >= 1902005 && source.getItemId() <= 1902007) || source.getItemId() == 1912005) && !chr.isCygnus()) {// Adventurer taming equipment
|
||||
return;
|
||||
}
|
||||
boolean itemChanged = false;
|
||||
if (ii.isUntradeableOnEquip(source.getItemId())) {
|
||||
short flag = source.getFlag(); // thanks BHB for noticing flags missing after equipping these
|
||||
flag |= ItemConstants.UNTRADEABLE;
|
||||
source.setFlag(flag);
|
||||
|
||||
itemChanged = true;
|
||||
}
|
||||
if (dst == -6) { // unequip the overall
|
||||
Item top = eqpdInv.getItem((short) -5);
|
||||
if (top != null && ItemConstants.isOverall(top.getItemId())) {
|
||||
if (eqpInv.isFull()) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return;
|
||||
}
|
||||
unequip(c, (byte) -5, eqpInv.getNextFreeSlot());
|
||||
}
|
||||
} else if (dst == -5) {
|
||||
final Item bottom = eqpdInv.getItem((short) -6);
|
||||
if (bottom != null && ItemConstants.isOverall(source.getItemId())) {
|
||||
if (eqpInv.isFull()) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return;
|
||||
}
|
||||
unequip(c, (byte) -6, eqpInv.getNextFreeSlot());
|
||||
}
|
||||
} else if (dst == -10) {// check if weapon is two-handed
|
||||
Item weapon = eqpdInv.getItem((short) -11);
|
||||
if (weapon != null && ii.isTwoHanded(weapon.getItemId())) {
|
||||
if (eqpInv.isFull()) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return;
|
||||
}
|
||||
unequip(c, (byte) -11, eqpInv.getNextFreeSlot());
|
||||
}
|
||||
} else if (dst == -11) {
|
||||
Item shield = eqpdInv.getItem((short) -10);
|
||||
if (shield != null && ii.isTwoHanded(source.getItemId())) {
|
||||
if (eqpInv.isFull()) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
c.announce(MaplePacketCreator.getShowInventoryFull());
|
||||
return;
|
||||
}
|
||||
unequip(c, (byte) -10, eqpInv.getNextFreeSlot());
|
||||
}
|
||||
}
|
||||
if (dst == -18) {
|
||||
if (chr.getMount() != null) {
|
||||
chr.getMount().setItemId(source.getItemId());
|
||||
}
|
||||
}
|
||||
|
||||
//1112413, 1112414, 1112405 (Lilin's Ring)
|
||||
source = (Equip) eqpInv.getItem(src);
|
||||
eqpInv.removeSlot(src);
|
||||
|
||||
Equip target;
|
||||
eqpdInv.lockInventory();
|
||||
try {
|
||||
target = (Equip) eqpdInv.getItem(dst);
|
||||
if (target != null) {
|
||||
chr.unequippedItem(target);
|
||||
eqpdInv.removeSlot(dst);
|
||||
}
|
||||
} finally {
|
||||
eqpdInv.unlockInventory();
|
||||
}
|
||||
|
||||
final List<ModifyInventory> mods = new ArrayList<>();
|
||||
if (itemChanged) {
|
||||
mods.add(new ModifyInventory(3, source));
|
||||
mods.add(new ModifyInventory(0, source.copy()));//to prevent crashes
|
||||
}
|
||||
|
||||
source.setPosition(dst);
|
||||
|
||||
eqpdInv.lockInventory();
|
||||
try {
|
||||
if (source.getRingId() > -1) {
|
||||
chr.getRingById(source.getRingId()).equip();
|
||||
}
|
||||
chr.equippedItem(source);
|
||||
eqpdInv.addItemFromDB(source);
|
||||
} finally {
|
||||
eqpdInv.unlockInventory();
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
target.setPosition(src);
|
||||
eqpInv.addItemFromDB(target);
|
||||
}
|
||||
if (chr.getBuffedValue(MapleBuffStat.BOOSTER) != null && ItemConstants.isWeapon(source.getItemId())) {
|
||||
chr.cancelBuffStats(MapleBuffStat.BOOSTER);
|
||||
}
|
||||
|
||||
mods.add(new ModifyInventory(2, source, src));
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, mods));
|
||||
chr.equipChanged();
|
||||
}
|
||||
|
||||
public static void unequip(MapleClient c, short src, short dst) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory eqpInv = chr.getInventory(MapleInventoryType.EQUIP);
|
||||
MapleInventory eqpdInv = chr.getInventory(MapleInventoryType.EQUIPPED);
|
||||
|
||||
Equip source = (Equip) eqpdInv.getItem(src);
|
||||
Equip target = (Equip) eqpInv.getItem(dst);
|
||||
if (dst < 0) {
|
||||
return;
|
||||
}
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
if (target != null && src <= 0) {
|
||||
c.announce(MaplePacketCreator.getInventoryFull());
|
||||
return;
|
||||
}
|
||||
|
||||
eqpdInv.lockInventory();
|
||||
try {
|
||||
if (source.getRingId() > -1) {
|
||||
chr.getRingById(source.getRingId()).unequip();
|
||||
}
|
||||
chr.unequippedItem(source);
|
||||
eqpdInv.removeSlot(src);
|
||||
} finally {
|
||||
eqpdInv.unlockInventory();
|
||||
}
|
||||
|
||||
if (target != null) {
|
||||
eqpInv.removeSlot(dst);
|
||||
}
|
||||
source.setPosition(dst);
|
||||
eqpInv.addItemFromDB(source);
|
||||
if (target != null) {
|
||||
target.setPosition(src);
|
||||
eqpdInv.addItemFromDB(target);
|
||||
}
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(2, source, src))));
|
||||
chr.equipChanged();
|
||||
}
|
||||
|
||||
private static boolean isDisappearingItemDrop(Item it) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
if (ii.isDropRestricted(it.getItemId())) {
|
||||
return true;
|
||||
} else if (ii.isCash(it.getItemId())) {
|
||||
if (YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_CASH) { // thanks Ari for noticing cash drops not available server-side
|
||||
return true;
|
||||
} else if (ItemConstants.isPet(it.getItemId()) && YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_PET) {
|
||||
return true;
|
||||
}
|
||||
} else if (isDroppedItemRestricted(it)) {
|
||||
return true;
|
||||
} else if (ItemConstants.isWeddingRing(it.getItemId())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void drop(MapleClient c, MapleInventoryType type, short src, short quantity) {
|
||||
if (src < 0) {
|
||||
type = MapleInventoryType.EQUIPPED;
|
||||
}
|
||||
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleInventory inv = chr.getInventory(type);
|
||||
Item source = inv.getItem(src);
|
||||
|
||||
if (chr.getTrade() != null || chr.getMiniGame() != null || source == null) { //Only check needed would prob be merchants (to see if the player is in one)
|
||||
return;
|
||||
}
|
||||
int itemId = source.getItemId();
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
if ((!ItemConstants.isRechargeable(itemId) && source.getQuantity() < quantity) || quantity < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int petid = source.getPetId();
|
||||
if (petid > -1) {
|
||||
int petIdx = chr.getPetIndex(petid);
|
||||
if(petIdx > -1) {
|
||||
MaplePet pet = chr.getPet(petIdx);
|
||||
chr.unequipPet(pet, true);
|
||||
}
|
||||
}
|
||||
|
||||
Point dropPos = new Point(chr.getPosition());
|
||||
if (quantity < source.getQuantity() && !ItemConstants.isRechargeable(itemId)) {
|
||||
Item target = source.copy();
|
||||
target.setQuantity(quantity);
|
||||
source.setQuantity((short) (source.getQuantity() - quantity));
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(1, source))));
|
||||
|
||||
if (ItemConstants.isNewYearCardEtc(itemId)) {
|
||||
if(itemId == 4300000) {
|
||||
NewYearCardRecord.removeAllNewYearCard(true, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4300000);
|
||||
} else {
|
||||
NewYearCardRecord.removeAllNewYearCard(false, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4301000);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDisappearingItemDrop(target)) {
|
||||
map.disappearingItemDrop(chr, chr, target, dropPos);
|
||||
} else {
|
||||
map.spawnItemDrop(chr, chr, target, dropPos, true, true);
|
||||
}
|
||||
} else {
|
||||
if (type == MapleInventoryType.EQUIPPED) {
|
||||
inv.lockInventory();
|
||||
try {
|
||||
chr.unequippedItem((Equip) source);
|
||||
inv.removeSlot(src);
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
} else {
|
||||
inv.removeSlot(src);
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(3, source))));
|
||||
if (src < 0) {
|
||||
chr.equipChanged();
|
||||
} else if (ItemConstants.isNewYearCardEtc(itemId)) {
|
||||
if (itemId == 4300000) {
|
||||
NewYearCardRecord.removeAllNewYearCard(true, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4300000);
|
||||
} else {
|
||||
NewYearCardRecord.removeAllNewYearCard(false, chr);
|
||||
c.getAbstractPlayerInteraction().removeAll(4301000);
|
||||
}
|
||||
}
|
||||
|
||||
if (isDisappearingItemDrop(source)) {
|
||||
map.disappearingItemDrop(chr, chr, source, dropPos);
|
||||
} else {
|
||||
map.spawnItemDrop(chr, chr, source, dropPos, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
int quantityNow = chr.getItemQuantity(itemId, false);
|
||||
if (itemId == chr.getItemEffect()) {
|
||||
if (quantityNow <= 0) {
|
||||
chr.setItemEffect(0);
|
||||
map.broadcastMessage(MaplePacketCreator.itemEffect(chr.getId(), 0));
|
||||
}
|
||||
} else if (itemId == 5370000 || itemId == 5370001) {
|
||||
if (source.getQuantity() <= 0) {
|
||||
chr.setChalkboard(null);
|
||||
}
|
||||
} else if (itemId == 4031868) {
|
||||
chr.updateAriantScore(quantityNow);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isDroppedItemRestricted(Item it) {
|
||||
return YamlConfig.config.server.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable();
|
||||
}
|
||||
|
||||
public static boolean isSandboxItem(Item it) {
|
||||
return (it.getFlag() & ItemConstants.SANDBOX) == ItemConstants.SANDBOX;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2019 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.inventory.manipulator;
|
||||
|
||||
import constants.inventory.ItemConstants;
|
||||
import client.inventory.Item;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class MapleKarmaManipulator {
|
||||
private static short getKarmaFlag(Item item) {
|
||||
return item.getItemType() == 1 ? ItemConstants.KARMA_EQP : ItemConstants.KARMA_USE;
|
||||
}
|
||||
|
||||
public static boolean hasKarmaFlag(Item item) {
|
||||
short karmaFlag = getKarmaFlag(item);
|
||||
return (item.getFlag() & karmaFlag) == karmaFlag;
|
||||
}
|
||||
|
||||
public static void toggleKarmaFlagToUntradeable(Item item) {
|
||||
short karmaFlag = getKarmaFlag(item);
|
||||
short flag = item.getFlag();
|
||||
|
||||
if ((flag & karmaFlag) == karmaFlag) {
|
||||
flag ^= karmaFlag;
|
||||
flag |= ItemConstants.UNTRADEABLE;
|
||||
|
||||
item.setFlag((byte) flag);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setKarmaFlag(Item item) {
|
||||
short karmaFlag = getKarmaFlag(item);
|
||||
short flag = item.getFlag();
|
||||
|
||||
flag |= karmaFlag;
|
||||
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);
|
||||
item.setFlag((byte) flag);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user