Switch to Maven file structure
This commit is contained in:
84
src/main/java/client/processor/action/BuybackProcessor.java
Normal file
84
src/main/java/client/processor/action/BuybackProcessor.java
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
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.processor.action; // thanks Alex for pointing out some package structures containing broad modules
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class BuybackProcessor {
|
||||
|
||||
public static void processBuyback(MapleClient c) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
boolean buyback;
|
||||
|
||||
c.lockClient();
|
||||
try {
|
||||
buyback = !chr.isAlive() && chr.couldBuyback();
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
|
||||
if (buyback) {
|
||||
String jobString;
|
||||
switch(chr.getJobStyle()) {
|
||||
case WARRIOR:
|
||||
jobString = "warrior";
|
||||
break;
|
||||
|
||||
case MAGICIAN:
|
||||
jobString = "magician";
|
||||
break;
|
||||
|
||||
case BOWMAN:
|
||||
jobString = "bowman";
|
||||
break;
|
||||
|
||||
case THIEF:
|
||||
jobString = "thief";
|
||||
break;
|
||||
|
||||
case BRAWLER:
|
||||
case GUNSLINGER:
|
||||
jobString = "pirate";
|
||||
break;
|
||||
|
||||
default:
|
||||
jobString = "beginner";
|
||||
}
|
||||
|
||||
chr.healHpMp();
|
||||
chr.purgeDebuffs();
|
||||
chr.broadcastStance(chr.isFacingLeft() ? 5 : 4);
|
||||
|
||||
MapleMap map = chr.getMap();
|
||||
map.broadcastMessage(MaplePacketCreator.playSound("Buyback/" + jobString));
|
||||
map.broadcastMessage(MaplePacketCreator.earnTitleMessage(chr.getName() + " just bought back into the game!"));
|
||||
|
||||
chr.announce(MaplePacketCreator.showBuybackEffect());
|
||||
map.broadcastMessage(chr, MaplePacketCreator.showForeignBuybackEffect(chr.getId()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
441
src/main/java/client/processor/action/MakerProcessor.java
Normal file
441
src/main/java/client/processor/action/MakerProcessor.java
Normal file
@@ -0,0 +1,441 @@
|
||||
/*
|
||||
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.processor.action;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
import constants.game.GameConstants;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import server.MakerItemFactory;
|
||||
import server.MakerItemFactory.MakerItemCreateEntry;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public class MakerProcessor {
|
||||
|
||||
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
public static void makerAction(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
int type = slea.readInt();
|
||||
int toCreate = slea.readInt();
|
||||
int toDisassemble = -1, pos = -1;
|
||||
boolean makerSucceeded = true;
|
||||
|
||||
MakerItemCreateEntry recipe;
|
||||
Map<Integer, Short> reagentids = new LinkedHashMap<>();
|
||||
int stimulantid = -1;
|
||||
|
||||
if(type == 3) { // building monster crystal
|
||||
int fromLeftover = toCreate;
|
||||
toCreate = ii.getMakerCrystalFromLeftover(toCreate);
|
||||
if(toCreate == -1) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(fromLeftover) + " is unavailable for Monster Crystal conversion."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover, toCreate);
|
||||
} else if(type == 4) { // disassembling
|
||||
slea.readInt(); // 1... probably inventory type
|
||||
pos = slea.readInt();
|
||||
|
||||
Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos);
|
||||
if(it != null && it.getItemId() == toCreate) {
|
||||
toDisassemble = toCreate;
|
||||
|
||||
Pair<Integer, List<Pair<Integer, Integer>>> p = generateDisassemblyInfo(toDisassemble);
|
||||
if(p != null) {
|
||||
recipe = MakerItemFactory.generateDisassemblyCrystalEntry(toDisassemble, p.getLeft(), p.getRight());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(ItemConstants.isEquipment(toCreate)) { // only equips uses stimulant and reagents
|
||||
if(slea.readByte() != 0) { // stimulant
|
||||
stimulantid = ii.getMakerStimulant(toCreate);
|
||||
if(!c.getAbstractPlayerInteraction().haveItem(stimulantid)) {
|
||||
stimulantid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int reagents = Math.min(slea.readInt(), getMakerReagentSlots(toCreate));
|
||||
for(int i = 0; i < reagents; i++) { // crystals
|
||||
int reagentid = slea.readInt();
|
||||
if(ItemConstants.isMakerReagent(reagentid)) {
|
||||
Short rs = reagentids.get(reagentid);
|
||||
if(rs == null) {
|
||||
reagentids.put(reagentid, (short) 1);
|
||||
} else {
|
||||
reagentids.put(reagentid, (short) (rs + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<Pair<Integer, Short>> toUpdate = new LinkedList<>();
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
int qty = c.getAbstractPlayerInteraction().getItemQuantity(r.getKey());
|
||||
|
||||
if(qty < r.getValue()) {
|
||||
toUpdate.add(new Pair<>(r.getKey(), (short) qty));
|
||||
}
|
||||
}
|
||||
|
||||
// remove those not present on player inventory
|
||||
if(!toUpdate.isEmpty()) {
|
||||
for(Pair<Integer, Short> rp : toUpdate) {
|
||||
if(rp.getRight() > 0) {
|
||||
reagentids.put(rp.getLeft(), rp.getRight());
|
||||
} else {
|
||||
reagentids.remove(rp.getLeft());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!reagentids.isEmpty()) {
|
||||
if(!removeOddMakerReagents(toCreate, reagentids)) {
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recipe = MakerItemFactory.getItemCreateEntry(toCreate, stimulantid, reagentids);
|
||||
}
|
||||
|
||||
short createStatus = getCreateStatus(c, recipe);
|
||||
|
||||
switch(createStatus) {
|
||||
case -1:// non-available for Maker itemid has been tried to forge
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill.");
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 1: // no items
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + ii.getName(toCreate) + "."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 2: // no meso
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 3: // no req level
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 4: // no req skill level
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
case 5: // inventory full
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
|
||||
c.announce(MaplePacketCreator.makerEnableActions());
|
||||
break;
|
||||
|
||||
default:
|
||||
if(toDisassemble != -1) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
|
||||
} else {
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight(), false);
|
||||
}
|
||||
}
|
||||
|
||||
int cost = recipe.getCost();
|
||||
if(stimulantid == -1 && reagentids.isEmpty()) {
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
|
||||
c.getPlayer().setCS(true);
|
||||
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), p.getRight().shortValue(), false);
|
||||
c.getPlayer().setCS(false);
|
||||
}
|
||||
} else {
|
||||
toCreate = recipe.getGainItems().get(0).getLeft();
|
||||
|
||||
if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1, false);
|
||||
if(!reagentids.isEmpty()) {
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()), false);
|
||||
}
|
||||
}
|
||||
|
||||
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
|
||||
makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
|
||||
}
|
||||
|
||||
// thanks inhyuk for noticing missing MAKER_RESULT packets
|
||||
if (type == 3) {
|
||||
c.announce(MaplePacketCreator.makerResultCrystal(recipe.getGainItems().get(0).getLeft(), recipe.getReqItems().get(0).getLeft()));
|
||||
} else if (type == 4) {
|
||||
c.announce(MaplePacketCreator.makerResultDesynth(recipe.getReqItems().get(0).getLeft(), recipe.getCost(), recipe.getGainItems()));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.makerResult(makerSucceeded, recipe.getGainItems().get(0).getLeft(), recipe.getGainItems().get(0).getRight(), recipe.getCost(), recipe.getReqItems(), stimulantid, new LinkedList<>(reagentids.keySet())));
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
|
||||
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
|
||||
|
||||
if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
|
||||
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checks and prevents hackers from PE'ing Maker operations with invalid operations
|
||||
private static boolean removeOddMakerReagents(int toCreate, Map<Integer, Short> reagentids) {
|
||||
Map<Integer, Integer> reagentType = new LinkedHashMap<>();
|
||||
List<Integer> toRemove = new LinkedList<>();
|
||||
|
||||
boolean isWeapon = ItemConstants.isWeapon(toCreate) || YamlConfig.config.server.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon
|
||||
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
int curRid = r.getKey();
|
||||
int type = r.getKey() / 100;
|
||||
|
||||
if(type < 42502 && !isWeapon) { // only weapons should gain w.att/m.att from these.
|
||||
return false; //toRemove.add(curRid);
|
||||
} else {
|
||||
Integer tableRid = reagentType.get(type);
|
||||
|
||||
if(tableRid != null) {
|
||||
if(tableRid < curRid) {
|
||||
toRemove.add(tableRid);
|
||||
reagentType.put(type, curRid);
|
||||
} else {
|
||||
toRemove.add(curRid);
|
||||
}
|
||||
} else {
|
||||
reagentType.put(type, curRid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removing less effective gems of repeated type
|
||||
for(Integer i : toRemove) {
|
||||
reagentids.remove(i);
|
||||
}
|
||||
|
||||
// the Maker skill will use only one of each gem
|
||||
for(Integer i : reagentids.keySet()) {
|
||||
reagentids.put(i, (short) 1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int getMakerReagentSlots(int itemId) {
|
||||
try {
|
||||
int eqpLevel = ii.getEquipLevelReq(itemId);
|
||||
|
||||
if(eqpLevel < 78) {
|
||||
return 1;
|
||||
} else if(eqpLevel >= 78 && eqpLevel < 108) {
|
||||
return 2;
|
||||
} else {
|
||||
return 3;
|
||||
}
|
||||
} catch(NullPointerException npe) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<Integer, List<Pair<Integer, Integer>>> generateDisassemblyInfo(int itemId) {
|
||||
int recvFee = ii.getMakerDisassembledFee(itemId);
|
||||
if(recvFee > -1) {
|
||||
List<Pair<Integer, Integer>> gains = ii.getMakerDisassembledItems(itemId);
|
||||
if(!gains.isEmpty()) {
|
||||
return new Pair<>(recvFee, gains);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int getMakerSkillLevel(MapleCharacter chr) {
|
||||
return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007);
|
||||
}
|
||||
|
||||
private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) {
|
||||
if(recipe.isInvalid()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(!hasItems(c, recipe)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(c.getPlayer().getMeso() < recipe.getCost()) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(c.getPlayer().getLevel() < recipe.getReqLevel()) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if(getMakerSkillLevel(c.getPlayer()) < recipe.getReqSkillLevel()) {
|
||||
return 4;
|
||||
}
|
||||
|
||||
List<Integer> addItemids = new LinkedList<>();
|
||||
List<Integer> addQuantity = new LinkedList<>();
|
||||
List<Integer> rmvItemids = new LinkedList<>();
|
||||
List<Integer> rmvQuantity = new LinkedList<>();
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
rmvItemids.add(p.getLeft());
|
||||
rmvQuantity.add(p.getRight());
|
||||
}
|
||||
|
||||
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
|
||||
addItemids.add(p.getLeft());
|
||||
addQuantity.add(p.getRight());
|
||||
}
|
||||
|
||||
if (!c.getAbstractPlayerInteraction().canHoldAllAfterRemoving(addItemids, addQuantity, rmvItemids, rmvQuantity)) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean hasItems(MapleClient c, MakerItemCreateEntry recipe) {
|
||||
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
|
||||
int itemId = p.getLeft();
|
||||
if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).countById(itemId) < p.getRight()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean addBoostedMakerItem(MapleClient c, int itemid, int stimulantid, Map<Integer, Short> reagentids) {
|
||||
if(stimulantid != -1 && !ii.rollSuccessChance(90.0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Item item = ii.getEquipById(itemid);
|
||||
if(item == null) return false;
|
||||
|
||||
Equip eqp = (Equip)item;
|
||||
if(ItemConstants.isAccessory(item.getItemId()) && eqp.getUpgradeSlots() <= 0) eqp.setUpgradeSlots(3);
|
||||
|
||||
if(YamlConfig.config.server.USE_ENHANCED_CRAFTING == true) {
|
||||
if(!(c.getPlayer().isGM() && YamlConfig.config.server.USE_PERFECT_GM_SCROLL)) {
|
||||
eqp.setUpgradeSlots((byte)(eqp.getUpgradeSlots() + 1));
|
||||
}
|
||||
item = MapleItemInformationProvider.getInstance().scrollEquipWithId(eqp, 2049100, true, 2049100, c.getPlayer().isGM());
|
||||
}
|
||||
|
||||
if(!reagentids.isEmpty()) {
|
||||
Map<String, Integer> stats = new LinkedHashMap<>();
|
||||
List<Short> randOption = new LinkedList<>();
|
||||
List<Short> randStat = new LinkedList<>();
|
||||
|
||||
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
|
||||
Pair<String, Integer> reagentBuff = ii.getMakerReagentStatUpgrade(r.getKey());
|
||||
|
||||
if(reagentBuff != null) {
|
||||
String s = reagentBuff.getLeft();
|
||||
|
||||
if(s.substring(0, 4).contains("rand")) {
|
||||
if(s.substring(4).equals("Stat")) {
|
||||
randStat.add((short) (reagentBuff.getRight() * r.getValue()));
|
||||
} else {
|
||||
randOption.add((short) (reagentBuff.getRight() * r.getValue()));
|
||||
}
|
||||
} else {
|
||||
String stat = s.substring(3);
|
||||
|
||||
if(!stat.equals("ReqLevel")) { // improve req level... really?
|
||||
switch (stat) {
|
||||
case "MaxHP":
|
||||
stat = "MHP";
|
||||
break;
|
||||
|
||||
case "MaxMP":
|
||||
stat = "MMP";
|
||||
break;
|
||||
}
|
||||
|
||||
Integer d = stats.get(stat);
|
||||
if(d == null) {
|
||||
stats.put(stat, reagentBuff.getRight() * r.getValue());
|
||||
} else {
|
||||
stats.put(stat, d + (reagentBuff.getRight() * r.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ii.improveEquipStats(eqp, stats);
|
||||
|
||||
for(Short sh : randStat) {
|
||||
ii.scrollOptionEquipWithChaos(eqp, sh, false);
|
||||
}
|
||||
|
||||
for(Short sh : randOption) {
|
||||
ii.scrollOptionEquipWithChaos(eqp, sh, true);
|
||||
}
|
||||
}
|
||||
|
||||
if(stimulantid != -1) {
|
||||
eqp = ii.randomizeUpgradeStats(eqp);
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false, -1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
190
src/main/java/client/processor/action/PetAutopotProcessor.java
Normal file
190
src/main/java/client/processor/action/PetAutopotProcessor.java
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.action;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import config.YamlConfig;
|
||||
import java.util.List;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleStatEffect;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan - multi-pot consumption feature
|
||||
*/
|
||||
public class PetAutopotProcessor {
|
||||
|
||||
private static class AutopotAction {
|
||||
|
||||
private MapleClient c;
|
||||
private short slot;
|
||||
private int itemId;
|
||||
|
||||
private Item toUse;
|
||||
private List<Item> toUseList;
|
||||
|
||||
private boolean hasHpGain, hasMpGain;
|
||||
private int maxHp, maxMp, curHp, curMp;
|
||||
private double incHp, incMp;
|
||||
|
||||
private boolean cursorOnNextAvailablePot(MapleCharacter chr) {
|
||||
if(toUseList == null) {
|
||||
toUseList = chr.getInventory(MapleInventoryType.USE).linkedListById(itemId);
|
||||
}
|
||||
|
||||
toUse = null;
|
||||
while(!toUseList.isEmpty()) {
|
||||
Item it = toUseList.remove(0);
|
||||
|
||||
if(it.getQuantity() > 0) {
|
||||
toUse = it;
|
||||
slot = it.getPosition();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public AutopotAction(MapleClient c, short slot, int itemId) {
|
||||
this.c = c;
|
||||
this.slot = slot;
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
MapleClient c = this.c;
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if (!chr.isAlive()) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
int useCount = 0, qtyCount = 0;
|
||||
MapleStatEffect stat = null;
|
||||
|
||||
maxHp = chr.getCurrentMaxHp();
|
||||
maxMp = chr.getCurrentMaxMp();
|
||||
|
||||
curHp = chr.getHp();
|
||||
curMp = chr.getMp();
|
||||
|
||||
MapleInventory useInv = chr.getInventory(MapleInventoryType.USE);
|
||||
useInv.lockInventory();
|
||||
try {
|
||||
toUse = useInv.getItem(slot);
|
||||
if (toUse != null) {
|
||||
if (toUse.getItemId() != itemId) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
toUseList = null;
|
||||
|
||||
// from now on, toUse becomes the "cursor" for the current pot being used
|
||||
if (toUse.getQuantity() <= 0) {
|
||||
if (!cursorOnNextAvailablePot(chr)) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId());
|
||||
hasHpGain = stat.getHp() > 0 || stat.getHpRate() > 0.0;
|
||||
hasMpGain = stat.getMp() > 0 || stat.getMpRate() > 0.0;
|
||||
|
||||
incHp = stat.getHp();
|
||||
if(incHp <= 0 && hasHpGain) incHp = Math.ceil(maxHp * stat.getHpRate());
|
||||
|
||||
incMp = stat.getMp();
|
||||
if(incMp <= 0 && hasMpGain) incMp = Math.ceil(maxMp * stat.getMpRate());
|
||||
|
||||
if (YamlConfig.config.server.USE_COMPULSORY_AUTOPOT) {
|
||||
if (hasHpGain) {
|
||||
double hpRatio = (YamlConfig.config.server.PET_AUTOHP_RATIO * maxHp) - curHp;
|
||||
if (hpRatio > 0.0) {
|
||||
qtyCount = (int) Math.ceil(hpRatio / incHp);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasMpGain) {
|
||||
double mpRatio = ((YamlConfig.config.server.PET_AUTOMP_RATIO * maxMp) - curMp);
|
||||
if (mpRatio > 0.0) {
|
||||
qtyCount = Math.max(qtyCount, (int) Math.ceil(mpRatio / incMp));
|
||||
}
|
||||
}
|
||||
|
||||
if (qtyCount < 0) { // thanks Flint, Kevs for noticing an issue where negative counts were getting achieved
|
||||
qtyCount = 0;
|
||||
}
|
||||
} else {
|
||||
qtyCount = 1; // non-compulsory autopot concept thanks to marcuswoon
|
||||
}
|
||||
|
||||
while (true) {
|
||||
short qtyToUse = (short) Math.min(qtyCount, toUse.getQuantity());
|
||||
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, qtyToUse, false);
|
||||
|
||||
curHp += (incHp * qtyToUse);
|
||||
curMp += (incMp * qtyToUse);
|
||||
|
||||
useCount += qtyToUse;
|
||||
qtyCount -= qtyToUse;
|
||||
|
||||
if(toUse.getQuantity() == 0 && qtyCount > 0) {
|
||||
// depleted out the current slot, fetch for more
|
||||
|
||||
if(!cursorOnNextAvailablePot(chr)) {
|
||||
break; // no more pots available
|
||||
}
|
||||
} else {
|
||||
break; // gracefully finished it's job, quit the loop
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
useInv.unlockInventory();
|
||||
}
|
||||
|
||||
if (stat != null) {
|
||||
for (int i = 0; i < useCount; i++) {
|
||||
stat.applyTo(chr);
|
||||
}
|
||||
}
|
||||
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
public static void runAutopotAction(MapleClient c, short slot, int itemid) {
|
||||
AutopotAction action = new AutopotAction(c, slot, itemid);
|
||||
action.run();
|
||||
}
|
||||
|
||||
}
|
||||
99
src/main/java/client/processor/action/SpawnPetProcessor.java
Normal file
99
src/main/java/client/processor/action/SpawnPetProcessor.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.processor.action;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.MaplePet;
|
||||
import client.SkillFactory;
|
||||
import provider.MapleDataTool;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import java.io.File;
|
||||
import provider.MapleDataProvider;
|
||||
import provider.MapleDataProviderFactory;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - just added locking on OdinMS' SpawnPetHandler method body
|
||||
*/
|
||||
public class SpawnPetProcessor {
|
||||
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
|
||||
|
||||
public static void processSpawnPet(MapleClient c, byte slot, boolean lead) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MaplePet pet = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getPet();
|
||||
if (pet == null) return;
|
||||
|
||||
int petid = pet.getItemId();
|
||||
if (petid == 5000028 || petid == 5000047) //Handles Dragon AND Robos
|
||||
{
|
||||
if (chr.haveItem(petid + 1)) {
|
||||
chr.dropMessage(5, "You can't hatch your " + (petid == 5000028 ? "Dragon egg" : "Robo egg") + " if you already have a Baby " + (petid == 5000028 ? "Dragon." : "Robo."));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
} else {
|
||||
int evolveid = MapleDataTool.getInt("info/evol1", dataRoot.getData("Pet/" + petid + ".img"));
|
||||
int petId = MaplePet.createPet(evolveid);
|
||||
if (petId == -1) {
|
||||
return;
|
||||
}
|
||||
long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
|
||||
MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (chr.getPetIndex(pet) != -1) {
|
||||
chr.unequipPet(pet, true);
|
||||
} else {
|
||||
if (chr.getSkillLevel(SkillFactory.getSkill(8)) == 0 && chr.getPet(0) != null) {
|
||||
chr.unequipPet(chr.getPet(0), false);
|
||||
}
|
||||
if (lead) {
|
||||
chr.shiftPetsRight();
|
||||
}
|
||||
Point pos = chr.getPosition();
|
||||
pos.y -= 12;
|
||||
pet.setPos(pos);
|
||||
pet.setFh(chr.getMap().getFootholds().findBelow(pet.getPos()).getId());
|
||||
pet.setStance(0);
|
||||
pet.setSummoned(true);
|
||||
pet.saveToDb();
|
||||
chr.addPet(pet);
|
||||
chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
|
||||
c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
chr.commitExcludedItems();
|
||||
chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
561
src/main/java/client/processor/npc/DueyProcessor.java
Normal file
561
src/main/java/client/processor/npc/DueyProcessor.java
Normal file
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.npc;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import net.server.channel.Channel;
|
||||
import server.DueyPackage;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleTrade;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - synchronization of Duey modules
|
||||
*/
|
||||
public class DueyProcessor {
|
||||
|
||||
public enum Actions {
|
||||
TOSERVER_RECV_ITEM(0x00),
|
||||
TOSERVER_SEND_ITEM(0x02),
|
||||
TOSERVER_CLAIM_PACKAGE(0x04),
|
||||
TOSERVER_REMOVE_PACKAGE(0x05),
|
||||
TOSERVER_CLOSE_DUEY(0x07),
|
||||
TOCLIENT_OPEN_DUEY(0x08),
|
||||
TOCLIENT_SEND_ENABLE_ACTIONS(0x09),
|
||||
TOCLIENT_SEND_NOT_ENOUGH_MESOS(0x0A),
|
||||
TOCLIENT_SEND_INCORRECT_REQUEST(0x0B),
|
||||
TOCLIENT_SEND_NAME_DOES_NOT_EXIST(0x0C),
|
||||
TOCLIENT_SEND_SAMEACC_ERROR(0x0D),
|
||||
TOCLIENT_SEND_RECEIVER_STORAGE_FULL(0x0E),
|
||||
TOCLIENT_SEND_RECEIVER_UNABLE_TO_RECV(0x0F),
|
||||
TOCLIENT_SEND_RECEIVER_STORAGE_WITH_UNIQUE(0x10),
|
||||
TOCLIENT_SEND_MESO_LIMIT(0x11),
|
||||
TOCLIENT_SEND_SUCCESSFULLY_SENT(0x12),
|
||||
TOCLIENT_RECV_UNKNOWN_ERROR(0x13),
|
||||
TOCLIENT_RECV_ENABLE_ACTIONS(0x14),
|
||||
TOCLIENT_RECV_NO_FREE_SLOTS(0x15),
|
||||
TOCLIENT_RECV_RECEIVER_WITH_UNIQUE(0x16),
|
||||
TOCLIENT_RECV_SUCCESSFUL_MSG(0x17),
|
||||
TOCLIENT_RECV_PACKAGE_MSG(0x1B);
|
||||
final byte code;
|
||||
|
||||
private Actions(int code) {
|
||||
this.code = (byte) code;
|
||||
}
|
||||
|
||||
public byte getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<Integer, Integer> getAccountCharacterIdFromCNAME(String name) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id,accountid FROM characters WHERE name = ?");
|
||||
ps.setString(1, name);
|
||||
|
||||
Pair<Integer, Integer> id_ = null;
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
id_ = new Pair<>(rs.getInt("accountid"), rs.getInt("id"));
|
||||
}
|
||||
}
|
||||
ps.close();
|
||||
con.close();
|
||||
return id_;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void showDueyNotification(MapleClient c, MapleCharacter player) {
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
PreparedStatement pss = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("SELECT SenderName, Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC");
|
||||
ps.setInt(1, player.getId());
|
||||
rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
try {
|
||||
Connection con2 = DatabaseConnection.getConnection();
|
||||
pss = con2.prepareStatement("UPDATE dueypackages SET Checked = 0 where ReceiverId = ?");
|
||||
pss.setInt(1, player.getId());
|
||||
pss.executeUpdate();
|
||||
pss.close();
|
||||
con2.close();
|
||||
|
||||
c.announce(MaplePacketCreator.sendDueyParcelReceived(rs.getString("SenderName"), rs.getInt("Type") == 1));
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
if (rs != null) {
|
||||
rs.close();
|
||||
}
|
||||
if (pss != null) {
|
||||
pss.close();
|
||||
}
|
||||
if (ps != null) {
|
||||
ps.close();
|
||||
}
|
||||
if (con != null) {
|
||||
con.close();
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void deletePackageFromInventoryDB(Connection con, int packageId) throws SQLException {
|
||||
ItemFactory.DUEY.saveItems(new LinkedList<Pair<Item, MapleInventoryType>>(), packageId, con);
|
||||
}
|
||||
|
||||
private static void removePackageFromDB(int packageId) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
PreparedStatement ps = con.prepareStatement("DELETE FROM dueypackages WHERE PackageId = ?");
|
||||
ps.setInt(1, packageId);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
deletePackageFromInventoryDB(con, packageId);
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static DueyPackage getPackageFromDB(ResultSet rs) {
|
||||
try {
|
||||
int packageId = rs.getInt("PackageId");
|
||||
|
||||
List<Pair<Item, MapleInventoryType>> dueyItems = ItemFactory.DUEY.loadItems(packageId, false);
|
||||
DueyPackage dueypack;
|
||||
|
||||
if (!dueyItems.isEmpty()) { // in a duey package there's only one item
|
||||
dueypack = new DueyPackage(packageId, dueyItems.get(0).getLeft());
|
||||
} else {
|
||||
dueypack = new DueyPackage(packageId);
|
||||
}
|
||||
|
||||
dueypack.setSender(rs.getString("SenderName"));
|
||||
dueypack.setMesos(rs.getInt("Mesos"));
|
||||
dueypack.setSentTime(rs.getTimestamp("TimeStamp"), rs.getBoolean("Type"));
|
||||
dueypack.setMessage(rs.getString("Message"));
|
||||
|
||||
return dueypack;
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static List<DueyPackage> loadPackages(MapleCharacter chr) {
|
||||
List<DueyPackage> packages = new LinkedList<>();
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM dueypackages dp WHERE ReceiverId = ?")) {
|
||||
ps.setInt(1, chr.getId());
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
DueyPackage dueypack = getPackageFromDB(rs);
|
||||
if (dueypack == null) continue;
|
||||
|
||||
packages.add(dueypack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return packages;
|
||||
}
|
||||
|
||||
private static int createPackage(int mesos, String message, String sender, int toCid, boolean quick) {
|
||||
try {
|
||||
Connection con = null;
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Type, Checked) VALUES (?, ?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS);
|
||||
ps.setInt(1, toCid);
|
||||
ps.setString(2, sender);
|
||||
ps.setInt(3, mesos);
|
||||
ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
|
||||
ps.setString(5, message);
|
||||
ps.setInt(6, quick ? 1 : 0);
|
||||
|
||||
int updateRows = ps.executeUpdate();
|
||||
if (updateRows < 1) {
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int packageId;
|
||||
rs = ps.getGeneratedKeys();
|
||||
if (rs.next()) {
|
||||
packageId = rs.getInt(1);
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return packageId;
|
||||
} finally {
|
||||
if (rs != null && !rs.isClosed()) {
|
||||
rs.close();
|
||||
}
|
||||
|
||||
if (ps != null && !ps.isClosed()) {
|
||||
ps.close();
|
||||
}
|
||||
|
||||
if (con != null && !con.isClosed()) {
|
||||
con.close();
|
||||
}
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static boolean insertPackageItem(int packageId, Item item) {
|
||||
try {
|
||||
Pair<Item, MapleInventoryType> dueyItem = new Pair<>(item, MapleInventoryType.getByType(item.getItemType()));
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
ItemFactory.DUEY.saveItems(Collections.singletonList(dueyItem), packageId, con);
|
||||
con.close();
|
||||
|
||||
return true;
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static int addPackageItemFromInventory(int packageId, MapleClient c, byte invTypeId, short itemPos, short amount) {
|
||||
if (invTypeId > 0) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
|
||||
MapleInventoryType invType = MapleInventoryType.getByType(invTypeId);
|
||||
MapleInventory inv = c.getPlayer().getInventory(invType);
|
||||
|
||||
Item item;
|
||||
inv.lockInventory();
|
||||
try {
|
||||
item = inv.getItem(itemPos);
|
||||
if (item != null && item.getQuantity() >= amount) {
|
||||
if (item.isUntradeable() || ii.isUnmerchable(item.getItemId())) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ItemConstants.isRechargeable(item.getItemId())) {
|
||||
MapleInventoryManipulator.removeFromSlot(c, invType, itemPos, item.getQuantity(), true);
|
||||
} else {
|
||||
MapleInventoryManipulator.removeFromSlot(c, invType, itemPos, amount, true, false);
|
||||
}
|
||||
|
||||
item = item.copy();
|
||||
} else {
|
||||
return -2;
|
||||
}
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
item.setQuantity(amount);
|
||||
|
||||
if (!insertPackageItem(packageId, item)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
int fee = MapleTrade.getFee(sendMesos);
|
||||
if (!quick) {
|
||||
fee += 5000;
|
||||
} else if (!c.getPlayer().haveItem(5330000)) {
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with Quick Delivery, mesos " + sendMesos + " and amount " + amount);
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
long finalcost = (long) sendMesos + fee;
|
||||
if (finalcost < 0 || finalcost > Integer.MAX_VALUE || (amount < 1 && sendMesos == 0)) {
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with duey.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with mesos " + sendMesos + " and amount " + amount);
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
Pair<Integer, Integer> accIdCid;
|
||||
if (c.getPlayer().getMeso() >= finalcost) {
|
||||
accIdCid = getAccountCharacterIdFromCNAME(recipient);
|
||||
int recipientAccId = accIdCid.getLeft();
|
||||
if (recipientAccId != -1) {
|
||||
if (recipientAccId == c.getAccID()) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NOT_ENOUGH_MESOS.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
int recipientCid = accIdCid.getRight();
|
||||
if (recipientCid == -1) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (quick) {
|
||||
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5330000, (short) 1, false, false);
|
||||
}
|
||||
|
||||
int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid, quick);
|
||||
if (packageId == -1) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_ENABLE_ACTIONS.getCode()));
|
||||
return;
|
||||
}
|
||||
c.getPlayer().gainMeso((int) -finalcost, false);
|
||||
|
||||
int res = addPackageItemFromInventory(packageId, c, invTypeId, itemPos, amount);
|
||||
if (res == 0) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SUCCESSFULLY_SENT.getCode()));
|
||||
} else if (res > 0) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_ENABLE_ACTIONS.getCode()));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode()));
|
||||
}
|
||||
|
||||
MapleClient rClient = null;
|
||||
int channel = c.getWorldServer().find(recipient);
|
||||
if (channel > -1) {
|
||||
Channel rcserv = c.getWorldServer().getChannel(channel);
|
||||
if(rcserv != null) {
|
||||
MapleCharacter rChr = rcserv.getPlayerStorage().getCharacterByName(recipient);
|
||||
if(rChr != null) {
|
||||
rClient = rChr.getClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rClient != null && rClient.isLoggedIn() && !rClient.getPlayer().isAwayFromWorld()) {
|
||||
showDueyNotification(rClient, rClient.getPlayer());
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dueyRemovePackage(MapleClient c, int packageid, boolean playerRemove) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
removePackageFromDB(packageid);
|
||||
c.announce(MaplePacketCreator.removeItemFromDuey(playerRemove, packageid));
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dueyClaimPackage(MapleClient c, int packageId) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
try {
|
||||
DueyPackage dp = null;
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM dueypackages dp WHERE PackageId = ?")) {
|
||||
ps.setInt(1, packageId);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
dp = getPackageFromDB(rs);
|
||||
}
|
||||
}
|
||||
}
|
||||
con.close();
|
||||
|
||||
if (dp == null) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to receive package from duey with id " + packageId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp.isDeliveringTime()) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
Item dpItem = dp.getItem();
|
||||
if (dpItem != null) {
|
||||
if (!c.getPlayer().canHoldMeso(dp.getMesos())) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MapleInventoryManipulator.checkSpace(c, dpItem.getItemId(), dpItem.getQuantity(), dpItem.getOwner())) {
|
||||
int itemid = dpItem.getItemId();
|
||||
if(MapleItemInformationProvider.getInstance().isPickupRestricted(itemid) && c.getPlayer().getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) != null) {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_RECEIVER_WITH_UNIQUE.getCode()));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_NO_FREE_SLOTS.getCode()));
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
MapleInventoryManipulator.addFromDrop(c, dpItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
c.getPlayer().gainMeso(dp.getMesos(), false);
|
||||
|
||||
dueyRemovePackage(c, packageId, false);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dueySendTalk(MapleClient c, boolean quickDelivery) {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
long timeNow = System.currentTimeMillis();
|
||||
if(timeNow - c.getPlayer().getNpcCooldown() < YamlConfig.config.server.BLOCK_NPC_RACE_CONDT) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
c.getPlayer().setNpcCooldown(timeNow);
|
||||
|
||||
if (quickDelivery) {
|
||||
c.announce(MaplePacketCreator.sendDuey(0x1A, null));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendDuey(0x8, loadPackages(c.getPlayer())));
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void dueyCreatePackage(Item item, int mesos, String sender, int recipientCid) {
|
||||
int packageId = createPackage(mesos, null, sender, recipientCid, false);
|
||||
if (packageId != -1) {
|
||||
insertPackageItem(packageId, item);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runDueyExpireSchedule() {
|
||||
try {
|
||||
Calendar c = Calendar.getInstance();
|
||||
c.add(Calendar.DATE, -30);
|
||||
|
||||
Timestamp ts = new Timestamp(c.getTime().getTime());
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT `PackageId` FROM dueypackages WHERE `TimeStamp` < ?");
|
||||
ps.setTimestamp(1, ts);
|
||||
|
||||
List<Integer> toRemove = new LinkedList<>();
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
toRemove.add(rs.getInt("PackageId"));
|
||||
}
|
||||
}
|
||||
ps.close();
|
||||
|
||||
for (Integer pid : toRemove) {
|
||||
removePackageFromDB(pid);
|
||||
}
|
||||
|
||||
ps = con.prepareStatement("DELETE FROM dueypackages WHERE `TimeStamp` < ?");
|
||||
ps.setTimestamp(1, ts);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
327
src/main/java/client/processor/npc/FredrickProcessor.java
Normal file
327
src/main/java/client/processor/npc/FredrickProcessor.java
Normal file
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.npc;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import java.util.Collections;
|
||||
import net.server.Server;
|
||||
import net.server.world.World;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.maps.MapleHiredMerchant;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - synchronization of Fredrick modules and operation results
|
||||
*/
|
||||
public class FredrickProcessor {
|
||||
|
||||
private static int[] dailyReminders = new int[]{2, 5, 10, 15, 30, 60, 90, Integer.MAX_VALUE};
|
||||
|
||||
private static byte canRetrieveFromFredrick(MapleCharacter chr, List<Pair<Item, MapleInventoryType>> items) {
|
||||
if (!MapleInventory.checkSpotsAndOwnership(chr, items)) {
|
||||
List<Integer> itemids = new LinkedList<>();
|
||||
for (Pair<Item, MapleInventoryType> it : items) {
|
||||
itemids.add(it.getLeft().getItemId());
|
||||
}
|
||||
|
||||
if (chr.canHoldUniques(itemids)) {
|
||||
return 0x22;
|
||||
} else {
|
||||
return 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
int netMeso = chr.getMerchantNetMeso();
|
||||
if (netMeso > 0) {
|
||||
if (!chr.canHoldMeso(netMeso)) {
|
||||
return 0x1F;
|
||||
}
|
||||
} else {
|
||||
if (chr.getMeso() < -1 * netMeso) {
|
||||
return 0x21;
|
||||
}
|
||||
}
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
public static int timestampElapsedDays(Timestamp then, long timeNow) {
|
||||
return (int) ((timeNow - then.getTime()) / (1000 * 60 * 60 * 24));
|
||||
}
|
||||
|
||||
private static String fredrickReminderMessage(int daynotes) {
|
||||
String msg;
|
||||
|
||||
if (daynotes < 4) {
|
||||
msg = "Hi customer! I am Fredrick, the Union Chief of the Hired Merchant Union. A reminder that " + dailyReminders[daynotes] + " days have passed since you used our service. Please reclaim your stored goods at FM Entrance.";
|
||||
} else {
|
||||
msg = "Hi customer! I am Fredrick, the Union Chief of the Hired Merchant Union. " + dailyReminders[daynotes] + " days have passed since you used our service. Consider claiming back the items before we move them away for refund.";
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static void removeFredrickLog(int cid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
removeFredrickLog(con, cid);
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeFredrickLog(Connection con, int cid) throws SQLException {
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM `fredstorage` WHERE `cid` = ?")) {
|
||||
ps.setInt(1, cid);
|
||||
ps.execute();
|
||||
}
|
||||
}
|
||||
|
||||
public static void insertFredrickLog(int cid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
removeFredrickLog(con, cid);
|
||||
try (PreparedStatement ps = con.prepareStatement("INSERT INTO `fredstorage` (`cid`, `daynotes`, `timestamp`) VALUES (?, 0, ?)")) {
|
||||
ps.setInt(1, cid);
|
||||
ps.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
|
||||
ps.execute();
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void removeFredrickReminders(int cid) {
|
||||
removeFredrickReminders(Collections.singletonList(new Pair<>(cid, 0)));
|
||||
}
|
||||
|
||||
private static void removeFredrickReminders(List<Pair<Integer, Integer>> expiredCids) {
|
||||
List<String> expiredCnames = new LinkedList<>();
|
||||
for (Pair<Integer, Integer> id : expiredCids) {
|
||||
String name = MapleCharacter.getNameById(id.getLeft());
|
||||
if (name != null) {
|
||||
expiredCnames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM `notes` WHERE `from` LIKE ? AND `to` LIKE ?")) {
|
||||
ps.setString(1, "FREDRICK");
|
||||
|
||||
for (String cname : expiredCnames) {
|
||||
ps.setString(2, cname);
|
||||
ps.executeBatch();
|
||||
}
|
||||
}
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void runFredrickSchedule() {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
List<Pair<Integer, Integer>> expiredCids = new LinkedList<>();
|
||||
List<Pair<Pair<Integer, String>, Integer>> notifCids = new LinkedList<>();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM fredstorage f LEFT JOIN (SELECT id, name, world, lastLogoutTime FROM characters) AS c ON c.id = f.cid")) {
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
long curTime = System.currentTimeMillis();
|
||||
|
||||
while (rs.next()) {
|
||||
int cid = rs.getInt("cid");
|
||||
int world = rs.getInt("world");
|
||||
Timestamp ts = rs.getTimestamp("timestamp");
|
||||
int daynotes = Math.min(dailyReminders.length - 1, rs.getInt("daynotes"));
|
||||
|
||||
int elapsedDays = timestampElapsedDays(ts, curTime);
|
||||
if (elapsedDays > 100) {
|
||||
expiredCids.add(new Pair<>(cid, world));
|
||||
} else {
|
||||
int notifDay = dailyReminders[daynotes];
|
||||
|
||||
if (elapsedDays >= notifDay) {
|
||||
do {
|
||||
daynotes++;
|
||||
notifDay = dailyReminders[daynotes];
|
||||
} while (elapsedDays >= notifDay);
|
||||
|
||||
Timestamp logoutTs = rs.getTimestamp("lastLogoutTime");
|
||||
int inactivityDays = timestampElapsedDays(logoutTs, curTime);
|
||||
|
||||
if (inactivityDays < 7 || daynotes >= dailyReminders.length - 1) { // don't spam inactive players
|
||||
String name = rs.getString("name");
|
||||
notifCids.add(new Pair<>(new Pair<>(cid, name), daynotes));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!expiredCids.isEmpty()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM `inventoryitems` WHERE `type` = ? AND `characterid` = ?")) {
|
||||
ps.setInt(1, ItemFactory.MERCHANT.getValue());
|
||||
|
||||
for (Pair<Integer, Integer> cid : expiredCids) {
|
||||
ps.setInt(2, cid.getLeft());
|
||||
ps.addBatch();
|
||||
}
|
||||
|
||||
ps.executeBatch();
|
||||
}
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE `characters` SET `MerchantMesos` = 0 WHERE `id` = ?")) {
|
||||
for (Pair<Integer, Integer> cid : expiredCids) {
|
||||
ps.setInt(1, cid.getLeft());
|
||||
ps.addBatch();
|
||||
|
||||
World wserv = Server.getInstance().getWorld(cid.getRight());
|
||||
if (wserv != null) {
|
||||
MapleCharacter chr = wserv.getPlayerStorage().getCharacterById(cid.getLeft());
|
||||
if (chr != null) {
|
||||
chr.setMerchantMeso(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ps.executeBatch();
|
||||
}
|
||||
|
||||
removeFredrickReminders(expiredCids);
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM `fredstorage` WHERE `cid` = ?")) {
|
||||
for (Pair<Integer, Integer> cid : expiredCids) {
|
||||
ps.setInt(1, cid.getLeft());
|
||||
ps.addBatch();
|
||||
}
|
||||
|
||||
ps.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
if (!notifCids.isEmpty()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("UPDATE `fredstorage` SET `daynotes` = ? WHERE `cid` = ?")) {
|
||||
for (Pair<Pair<Integer, String>, Integer> cid : notifCids) {
|
||||
ps.setInt(1, cid.getRight());
|
||||
ps.setInt(2, cid.getLeft().getLeft());
|
||||
ps.addBatch();
|
||||
|
||||
String msg = fredrickReminderMessage(cid.getRight() - 1);
|
||||
MapleCharacter.sendNote(cid.getLeft().getRight(), "FREDRICK", msg, (byte) 0);
|
||||
}
|
||||
|
||||
ps.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean deleteFredrickItems(int cid) {
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("DELETE FROM `inventoryitems` WHERE `type` = ? AND `characterid` = ?")) {
|
||||
ps.setInt(1, ItemFactory.MERCHANT.getValue());
|
||||
ps.setInt(2, cid);
|
||||
ps.execute();
|
||||
}
|
||||
con.close();
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void fredrickRetrieveItems(MapleClient c) { // thanks Gustav for pointing out the dupe on Fredrick handling
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
List<Pair<Item, MapleInventoryType>> items;
|
||||
try {
|
||||
items = ItemFactory.MERCHANT.loadItems(chr.getId(), false);
|
||||
|
||||
byte response = canRetrieveFromFredrick(chr, items);
|
||||
if (response != 0) {
|
||||
chr.announce(MaplePacketCreator.fredrickMessage(response));
|
||||
return;
|
||||
}
|
||||
|
||||
chr.withdrawMerchantMesos();
|
||||
|
||||
if (deleteFredrickItems(chr.getId())) {
|
||||
MapleHiredMerchant merchant = chr.getHiredMerchant();
|
||||
|
||||
if(merchant != null)
|
||||
merchant.clearItems();
|
||||
|
||||
for (Pair<Item, MapleInventoryType> it : items) {
|
||||
Item item = it.getLeft();
|
||||
MapleInventoryManipulator.addFromDrop(chr.getClient(), item, false);
|
||||
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
}
|
||||
|
||||
chr.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E));
|
||||
removeFredrickLog(chr.getId());
|
||||
} else {
|
||||
chr.message("An unknown error has occured.");
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
207
src/main/java/client/processor/npc/StorageProcessor.java
Normal file
207
src/main/java/client/processor/npc/StorageProcessor.java
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
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.processor.npc;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventory;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleKarmaManipulator;
|
||||
import config.YamlConfig;
|
||||
import constants.inventory.ItemConstants;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MapleItemInformationProvider;
|
||||
import server.MapleStorage;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Matze
|
||||
* @author Ronan - inventory concurrency protection on storing items
|
||||
*/
|
||||
public class StorageProcessor {
|
||||
|
||||
public static void storageAction(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
MapleStorage storage = chr.getStorage();
|
||||
byte mode = slea.readByte();
|
||||
|
||||
if (chr.getLevel() < 15){
|
||||
chr.dropMessage(1, "You may only use the storage once you have reached level 15.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
if (mode == 4) { // take out
|
||||
byte type = slea.readByte();
|
||||
byte slot = slea.readByte();
|
||||
if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
|
||||
FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to work with storage slot " + slot);
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
slot = storage.getSlot(MapleInventoryType.getByType(type), slot);
|
||||
Item item = storage.getItem(slot);
|
||||
if (item != null) {
|
||||
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0C));
|
||||
return;
|
||||
}
|
||||
|
||||
int takeoutFee = storage.getTakeOutFee();
|
||||
if (chr.getMeso() < takeoutFee) {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
|
||||
return;
|
||||
} else {
|
||||
chr.gainMeso(-takeoutFee, false);
|
||||
}
|
||||
|
||||
if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
|
||||
if (storage.takeOut(item)) {
|
||||
chr.setUsedStorage();
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
MapleInventoryManipulator.addFromDrop(c, item, false);
|
||||
|
||||
String itemName = ii.getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
|
||||
storage.sendTakenOut(c, item.getInventoryType());
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0A));
|
||||
}
|
||||
}
|
||||
} else if (mode == 5) { // store
|
||||
short slot = slea.readShort();
|
||||
int itemId = slea.readInt();
|
||||
short quantity = slea.readShort();
|
||||
MapleInventoryType invType = ItemConstants.getInventoryType(itemId);
|
||||
MapleInventory inv = chr.getInventory(invType);
|
||||
if (slot < 1 || slot > inv.getSlotLimit()) { //player inv starts at one
|
||||
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
|
||||
FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to store item at slot " + slot);
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
if (quantity < 1) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
if (storage.isFull()) {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x11));
|
||||
return;
|
||||
}
|
||||
|
||||
int storeFee = storage.getStoreFee();
|
||||
if (chr.getMeso() < storeFee) {
|
||||
c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
|
||||
} else {
|
||||
Item item;
|
||||
|
||||
inv.lockInventory(); // thanks imbee for pointing a dupe within storage
|
||||
try {
|
||||
item = inv.getItem(slot);
|
||||
if (item != null && item.getItemId() == itemId && (item.getQuantity() >= quantity || ItemConstants.isRechargeable(itemId))) {
|
||||
if (ItemConstants.isWeddingRing(itemId) || ItemConstants.isWeddingToken(itemId)) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ItemConstants.isRechargeable(itemId)) {
|
||||
quantity = item.getQuantity();
|
||||
}
|
||||
|
||||
MapleInventoryManipulator.removeFromSlot(c, invType, slot, quantity, false);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
item = item.copy(); // thanks Robin Schulz & BHB88 for noticing a inventory glitch when storing items
|
||||
} finally {
|
||||
inv.unlockInventory();
|
||||
}
|
||||
|
||||
chr.gainMeso(-storeFee, false, true, false);
|
||||
|
||||
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
|
||||
item.setQuantity(quantity);
|
||||
|
||||
storage.store(item); // inside a critical section, "!(storage.isFull())" is still in effect...
|
||||
chr.setUsedStorage();
|
||||
|
||||
String itemName = ii.getName(item.getItemId());
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
|
||||
|
||||
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
|
||||
}
|
||||
} else if (mode == 6) { // arrange items
|
||||
if(YamlConfig.config.server.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
} else if (mode == 7) { // meso
|
||||
int meso = slea.readInt();
|
||||
int storageMesos = storage.getMeso();
|
||||
int playerMesos = chr.getMeso();
|
||||
if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
|
||||
if (meso < 0 && (storageMesos - meso) < 0) {
|
||||
meso = Integer.MIN_VALUE + storageMesos;
|
||||
if (meso < playerMesos) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
} else if (meso > 0 && (playerMesos + meso) < 0) {
|
||||
meso = Integer.MAX_VALUE - playerMesos;
|
||||
if (meso > storageMesos) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
}
|
||||
storage.setMeso(storageMesos - meso);
|
||||
chr.gainMeso(meso, false, true, false);
|
||||
chr.setUsedStorage();
|
||||
FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos");
|
||||
storage.sendMeso(c);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
} else if (mode == 8) {// close... unless the player decides to enter cash shop!
|
||||
storage.close();
|
||||
}
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
840
src/main/java/client/processor/stat/AssignAPProcessor.java
Normal file
840
src/main/java/client/processor/stat/AssignAPProcessor.java
Normal file
@@ -0,0 +1,840 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.stat;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleJob;
|
||||
import client.MapleStat;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import config.YamlConfig;
|
||||
import constants.skills.BlazeWizard;
|
||||
import constants.skills.Brawler;
|
||||
import constants.skills.DawnWarrior;
|
||||
import constants.skills.Magician;
|
||||
import constants.skills.ThunderBreaker;
|
||||
import constants.skills.Warrior;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import server.ThreadManager;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - synchronization of AP transaction modules
|
||||
*/
|
||||
public class AssignAPProcessor {
|
||||
|
||||
public static void APAutoAssignAction(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if (chr.getRemainingAp() < 1) return;
|
||||
|
||||
Collection<Item> equippedC = chr.getInventory(MapleInventoryType.EQUIPPED).list();
|
||||
|
||||
c.lockClient();
|
||||
try {
|
||||
int[] statGain = new int[4];
|
||||
int[] statUpdate = new int[4];
|
||||
statGain[0] = 0; statGain[1] = 0; statGain[2] = 0; statGain[3] = 0;
|
||||
|
||||
int remainingAp = chr.getRemainingAp();
|
||||
slea.skip(8);
|
||||
|
||||
if(YamlConfig.config.server.USE_SERVER_AUTOASSIGNER) {
|
||||
// --------- Ronan Lana's AUTOASSIGNER ---------
|
||||
// This method excels for assigning APs in such a way to cover all equipments AP requirements.
|
||||
byte opt = slea.readByte(); // useful for pirate autoassigning
|
||||
|
||||
int str = 0, dex = 0, luk = 0, int_ = 0;
|
||||
List<Short> eqpStrList = new ArrayList<>();
|
||||
List<Short> eqpDexList = new ArrayList<>();
|
||||
List<Short> eqpLukList = new ArrayList<>();
|
||||
|
||||
Equip nEquip;
|
||||
|
||||
for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item.
|
||||
nEquip = (Equip)item;
|
||||
if(nEquip.getStr() > 0) eqpStrList.add(nEquip.getStr());
|
||||
str += nEquip.getStr();
|
||||
|
||||
if(nEquip.getDex() > 0) eqpDexList.add(nEquip.getDex());
|
||||
dex += nEquip.getDex();
|
||||
|
||||
if(nEquip.getLuk() > 0) eqpLukList.add(nEquip.getLuk());
|
||||
luk += nEquip.getLuk();
|
||||
|
||||
//if(nEquip.getInt() > 0) eqpIntList.add(nEquip.getInt()); //not needed...
|
||||
int_ += nEquip.getInt();
|
||||
}
|
||||
|
||||
statUpdate[0] = chr.getStr();
|
||||
statUpdate[1] = chr.getDex();
|
||||
statUpdate[2] = chr.getLuk();
|
||||
statUpdate[3] = chr.getInt();
|
||||
|
||||
Collections.sort(eqpStrList, Collections.reverseOrder());
|
||||
Collections.sort(eqpDexList, Collections.reverseOrder());
|
||||
Collections.sort(eqpLukList, Collections.reverseOrder());
|
||||
|
||||
//Autoassigner looks up the 1st/2nd placed equips for their stats to calculate the optimal upgrade.
|
||||
int eqpStr = getNthHighestStat(eqpStrList, (short) 0) + getNthHighestStat(eqpStrList, (short) 1);
|
||||
int eqpDex = getNthHighestStat(eqpDexList, (short) 0) + getNthHighestStat(eqpDexList, (short) 1);
|
||||
int eqpLuk = getNthHighestStat(eqpLukList, (short) 0) + getNthHighestStat(eqpLukList, (short) 1);
|
||||
|
||||
//c.getPlayer().message("----------------------------------------");
|
||||
//c.getPlayer().message("SDL: s" + eqpStr + " d" + eqpDex + " l" + eqpLuk + " BASE STATS --> STR: " + chr.getStr() + " DEX: " + chr.getDex() + " INT: " + chr.getInt() + " LUK: " + chr.getLuk());
|
||||
//c.getPlayer().message("SUM EQUIP STATS -> STR: " + str + " DEX: " + dex + " LUK: " + luk + " INT: " + int_);
|
||||
|
||||
MapleJob stance = c.getPlayer().getJobStyle(opt);
|
||||
int prStat = 0, scStat = 0, trStat = 0, temp, tempAp = remainingAp, CAP;
|
||||
if (tempAp < 1) return;
|
||||
|
||||
MapleStat primary, secondary, tertiary = MapleStat.LUK;
|
||||
switch(stance) {
|
||||
case MAGICIAN:
|
||||
CAP = 165;
|
||||
scStat = (chr.getLevel() + 3) - (chr.getLuk() + luk - eqpLuk);
|
||||
if(scStat < 0) scStat = 0;
|
||||
scStat = Math.min(scStat, tempAp);
|
||||
|
||||
if(tempAp > scStat) tempAp -= scStat;
|
||||
else tempAp = 0;
|
||||
|
||||
prStat = tempAp;
|
||||
int_ = prStat;
|
||||
luk = scStat;
|
||||
str = 0; dex = 0;
|
||||
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) {
|
||||
temp = luk + chr.getLuk() - CAP;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.INT;
|
||||
secondary = MapleStat.LUK;
|
||||
tertiary = MapleStat.DEX;
|
||||
|
||||
break;
|
||||
|
||||
case BOWMAN:
|
||||
CAP = 125;
|
||||
scStat = (chr.getLevel() + 5) - (chr.getStr() + str - eqpStr);
|
||||
if(scStat < 0) scStat = 0;
|
||||
scStat = Math.min(scStat, tempAp);
|
||||
|
||||
if(tempAp > scStat) tempAp -= scStat;
|
||||
else tempAp = 0;
|
||||
|
||||
prStat = tempAp;
|
||||
dex = prStat;
|
||||
str = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.DEX;
|
||||
secondary = MapleStat.STR;
|
||||
|
||||
break;
|
||||
|
||||
case GUNSLINGER:
|
||||
case CROSSBOWMAN:
|
||||
CAP = 120;
|
||||
scStat = chr.getLevel() - (chr.getStr() + str - eqpStr);
|
||||
if(scStat < 0) scStat = 0;
|
||||
scStat = Math.min(scStat, tempAp);
|
||||
|
||||
if(tempAp > scStat) tempAp -= scStat;
|
||||
else tempAp = 0;
|
||||
|
||||
prStat = tempAp;
|
||||
dex = prStat;
|
||||
str = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.DEX;
|
||||
secondary = MapleStat.STR;
|
||||
|
||||
break;
|
||||
|
||||
case THIEF:
|
||||
CAP = 160;
|
||||
|
||||
scStat = 0;
|
||||
if(chr.getDex() < 80) {
|
||||
scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
|
||||
scStat = Math.min(80 - chr.getDex(), scStat);
|
||||
scStat = Math.min(tempAp, scStat);
|
||||
tempAp -= scStat;
|
||||
}
|
||||
|
||||
temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
scStat += temp;
|
||||
tempAp -= temp;
|
||||
|
||||
// thieves will upgrade STR as well only if a level-based threshold is reached.
|
||||
if(chr.getStr() >= Math.max(13, (int)(0.4 * chr.getLevel()))) {
|
||||
if(chr.getStr() < 50) {
|
||||
trStat = (chr.getLevel() - 10) - (chr.getStr() + str - eqpStr);
|
||||
if(trStat < 0) trStat = 0;
|
||||
|
||||
trStat = Math.min(50 - chr.getStr(), trStat);
|
||||
trStat = Math.min(tempAp, trStat);
|
||||
tempAp -= trStat;
|
||||
}
|
||||
|
||||
temp = (20 + (chr.getLevel() / 2)) - Math.max(50, trStat + chr.getStr() + str - eqpStr);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
trStat += temp;
|
||||
tempAp -= temp;
|
||||
}
|
||||
|
||||
prStat = tempAp;
|
||||
luk = prStat;
|
||||
dex = scStat;
|
||||
str = trStat;
|
||||
int_ = 0;
|
||||
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
|
||||
temp = dex + chr.getDex() - CAP;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
|
||||
temp = str + chr.getStr() - CAP;
|
||||
trStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.LUK;
|
||||
secondary = MapleStat.DEX;
|
||||
tertiary = MapleStat.STR;
|
||||
|
||||
break;
|
||||
|
||||
case BRAWLER:
|
||||
default: //warrior, beginner, ...
|
||||
CAP = 300;
|
||||
|
||||
boolean highDex = false; // thanks lucasziron & Vcoc for finding out DEX autoassigning poorly for STR-based characters
|
||||
if (chr.getLevel() < 40) {
|
||||
if (chr.getDex() >= (2 * chr.getLevel()) + 2) {
|
||||
highDex = true;
|
||||
}
|
||||
} else {
|
||||
if (chr.getDex() >= chr.getLevel() + 42) {
|
||||
highDex = true;
|
||||
}
|
||||
}
|
||||
|
||||
// other classes will start favoring more DEX only if a level-based threshold is reached.
|
||||
if(!highDex) {
|
||||
scStat = 0;
|
||||
if(chr.getDex() < 80) {
|
||||
scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
|
||||
scStat = Math.min(80 - chr.getDex(), scStat);
|
||||
scStat = Math.min(tempAp, scStat);
|
||||
tempAp -= scStat;
|
||||
}
|
||||
|
||||
temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
scStat += temp;
|
||||
tempAp -= temp;
|
||||
} else {
|
||||
scStat = 0;
|
||||
if(chr.getDex() < 96) {
|
||||
scStat = (int)(2.4 * chr.getLevel()) - (chr.getDex() + dex - eqpDex);
|
||||
if(scStat < 0) scStat = 0;
|
||||
|
||||
scStat = Math.min(96 - chr.getDex(), scStat);
|
||||
scStat = Math.min(tempAp, scStat);
|
||||
tempAp -= scStat;
|
||||
}
|
||||
|
||||
temp = 96 + (int)(1.2 * (chr.getLevel() - 40)) - Math.max(96, scStat + chr.getDex() + dex - eqpDex);
|
||||
if(temp < 0) temp = 0;
|
||||
temp = Math.min(tempAp, temp);
|
||||
scStat += temp;
|
||||
tempAp -= temp;
|
||||
}
|
||||
|
||||
prStat = tempAp;
|
||||
str = prStat;
|
||||
dex = scStat;
|
||||
int_ = 0; luk = 0;
|
||||
|
||||
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
|
||||
temp = dex + chr.getDex() - CAP;
|
||||
scStat -= temp;
|
||||
prStat += temp;
|
||||
}
|
||||
|
||||
primary = MapleStat.STR;
|
||||
secondary = MapleStat.DEX;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------------------
|
||||
|
||||
int extras = 0;
|
||||
|
||||
extras = gainStatByType(primary, statGain, prStat + extras, statUpdate);
|
||||
extras = gainStatByType(secondary, statGain, scStat + extras, statUpdate);
|
||||
extras = gainStatByType(tertiary, statGain, trStat + extras, statUpdate);
|
||||
|
||||
if(extras > 0) { //redistribute surplus in priority order
|
||||
extras = gainStatByType(primary, statGain, extras, statUpdate);
|
||||
extras = gainStatByType(secondary, statGain, extras, statUpdate);
|
||||
extras = gainStatByType(tertiary, statGain, extras, statUpdate);
|
||||
gainStatByType(getQuaternaryStat(stance), statGain, extras, statUpdate);
|
||||
}
|
||||
|
||||
chr.assignStrDexIntLuk(statGain[0], statGain[1], statGain[3], statGain[2]);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
//----------------------------------------------------------------------------------------
|
||||
|
||||
c.announce(MaplePacketCreator.serverNotice(1, "Better AP applications detected:\r\nSTR: +" + statGain[0] + "\r\nDEX: +" + statGain[1] + "\r\nINT: +" + statGain[3] + "\r\nLUK: +" + statGain[2]));
|
||||
} else {
|
||||
if(slea.available() < 16) {
|
||||
AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign.");
|
||||
|
||||
c.disconnect(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
int type = slea.readInt();
|
||||
int tempVal = slea.readInt();
|
||||
if (tempVal < 0 || tempVal > remainingAp) {
|
||||
return;
|
||||
}
|
||||
|
||||
gainStatByType(MapleStat.getBy5ByteEncoding(type), statGain, tempVal, statUpdate);
|
||||
}
|
||||
|
||||
chr.assignStrDexIntLuk(statGain[0], statGain[1], statGain[3], statGain[2]);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
|
||||
private static int getNthHighestStat(List<Short> statList, short rank) { // ranks from 0
|
||||
return(statList.size() <= rank ? 0 : statList.get(rank));
|
||||
}
|
||||
|
||||
private static int gainStatByType(MapleStat type, int[] statGain, int gain, int statUpdate[]) {
|
||||
if(gain <= 0) return 0;
|
||||
|
||||
int newVal = 0;
|
||||
if (type.equals(MapleStat.STR)) {
|
||||
newVal = statUpdate[0] + gain;
|
||||
if (newVal > YamlConfig.config.server.MAX_AP) {
|
||||
statGain[0] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
|
||||
statUpdate[0] = YamlConfig.config.server.MAX_AP;
|
||||
} else {
|
||||
statGain[0] += gain;
|
||||
statUpdate[0] = newVal;
|
||||
}
|
||||
} else if (type.equals(MapleStat.INT)) {
|
||||
newVal = statUpdate[3] + gain;
|
||||
if (newVal > YamlConfig.config.server.MAX_AP) {
|
||||
statGain[3] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
|
||||
statUpdate[3] = YamlConfig.config.server.MAX_AP;
|
||||
} else {
|
||||
statGain[3] += gain;
|
||||
statUpdate[3] = newVal;
|
||||
}
|
||||
} else if (type.equals(MapleStat.LUK)) {
|
||||
newVal = statUpdate[2] + gain;
|
||||
if (newVal > YamlConfig.config.server.MAX_AP) {
|
||||
statGain[2] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
|
||||
statUpdate[2] = YamlConfig.config.server.MAX_AP;
|
||||
} else {
|
||||
statGain[2] += gain;
|
||||
statUpdate[2] = newVal;
|
||||
}
|
||||
} else if (type.equals(MapleStat.DEX)) {
|
||||
newVal = statUpdate[1] + gain;
|
||||
if (newVal > YamlConfig.config.server.MAX_AP) {
|
||||
statGain[1] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
|
||||
statUpdate[1] = YamlConfig.config.server.MAX_AP;
|
||||
} else {
|
||||
statGain[1] += gain;
|
||||
statUpdate[1] = newVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (newVal > YamlConfig.config.server.MAX_AP) {
|
||||
return newVal - YamlConfig.config.server.MAX_AP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static MapleStat getQuaternaryStat(MapleJob stance) {
|
||||
if(stance != MapleJob.MAGICIAN) return MapleStat.INT;
|
||||
return MapleStat.STR;
|
||||
}
|
||||
|
||||
public static boolean APResetAction(MapleClient c, int APFrom, int APTo) {
|
||||
c.lockClient();
|
||||
try {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
|
||||
switch (APFrom) {
|
||||
case 64: // str
|
||||
if (player.getStr() < 5) {
|
||||
player.message("You don't have the minimum STR required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
if (!player.assignStr(-1)) {
|
||||
player.message("Couldn't execute AP reset operation.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 128: // dex
|
||||
if (player.getDex() < 5) {
|
||||
player.message("You don't have the minimum DEX required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
if (!player.assignDex(-1)) {
|
||||
player.message("Couldn't execute AP reset operation.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 256: // int
|
||||
if (player.getInt() < 5) {
|
||||
player.message("You don't have the minimum INT required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
if (!player.assignInt(-1)) {
|
||||
player.message("Couldn't execute AP reset operation.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 512: // luk
|
||||
if (player.getLuk() < 5) {
|
||||
player.message("You don't have the minimum LUK required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
if (!player.assignLuk(-1)) {
|
||||
player.message("Couldn't execute AP reset operation.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2048: // HP
|
||||
if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) {
|
||||
if (APTo != 8192) {
|
||||
player.message("You can only swap HP ability points to MP.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.getHpMpApUsed() < 1) {
|
||||
player.message("You don't have enough HPMP stat points to spend on AP Reset.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int hp = player.getMaxHp();
|
||||
int level_ = player.getLevel();
|
||||
if (hp < level_ * 14 + 148) {
|
||||
player.message("You don't have the minimum HP pool required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int curHp = player.getHp();
|
||||
int hplose = -takeHp(player.getJob());
|
||||
player.assignHP(hplose, -1);
|
||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||
player.updateHp(Math.max(1, curHp + hplose));
|
||||
}
|
||||
|
||||
break;
|
||||
case 8192: // MP
|
||||
if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) {
|
||||
if (APTo != 2048) {
|
||||
player.message("You can only swap MP ability points to HP.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (player.getHpMpApUsed() < 1) {
|
||||
player.message("You don't have enough HPMP stat points to spend on AP Reset.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int mp = player.getMaxMp();
|
||||
int level = player.getLevel();
|
||||
MapleJob job = player.getJob();
|
||||
|
||||
boolean canWash = true;
|
||||
if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) {
|
||||
canWash = false;
|
||||
} else if ((job.isA(MapleJob.FIGHTER) || job.isA(MapleJob.ARAN1)) && mp < 4 * level + 56) {
|
||||
canWash = false;
|
||||
} else if (job.isA(MapleJob.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) {
|
||||
canWash = false;
|
||||
} else if (mp < level * 14 + 148) {
|
||||
canWash = false;
|
||||
}
|
||||
|
||||
if (!canWash) {
|
||||
player.message("You don't have the minimum MP pool required to swap.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
int curMp = player.getMp();
|
||||
int mplose = -takeMp(job);
|
||||
player.assignMP(mplose, -1);
|
||||
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
|
||||
player.updateMp(Math.max(0, curMp + mplose));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, player));
|
||||
return false;
|
||||
}
|
||||
|
||||
addStat(player, APTo, true);
|
||||
return true;
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
|
||||
public static void APAssignAction(MapleClient c, int num) {
|
||||
c.lockClient();
|
||||
try {
|
||||
addStat(c.getPlayer(), num, false);
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean addStat(MapleCharacter chr, int apTo, boolean usedAPReset) {
|
||||
switch (apTo) {
|
||||
case 64:
|
||||
if (!chr.assignStr(1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 128: // Dex
|
||||
if (!chr.assignDex(1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 256: // Int
|
||||
if (!chr.assignInt(1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 512: // Luk
|
||||
if (!chr.assignLuk(1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 2048:
|
||||
if (!chr.assignHP(calcHpChange(chr, usedAPReset), 1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 8192:
|
||||
if (!chr.assignMP(calcMpChange(chr, usedAPReset), 1)) {
|
||||
chr.message("Couldn't execute AP assign operation.");
|
||||
chr.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chr.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, chr));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int calcHpChange(MapleCharacter player, boolean usedAPReset) {
|
||||
MapleJob job = player.getJob();
|
||||
int MaxHP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
|
||||
if(!usedAPReset) {
|
||||
Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP);
|
||||
int sLvl = player.getSkillLevel(increaseHP);
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxHP += increaseHP.getEffect(sLvl).getY();
|
||||
}
|
||||
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 20;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(18, 22);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 20;
|
||||
}
|
||||
} else if(job.isA(MapleJob.ARAN1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 20;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(26, 30);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 28;
|
||||
}
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 6;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(5, 9);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 6;
|
||||
}
|
||||
} else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 16;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(14, 18);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 16;
|
||||
}
|
||||
} else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 16;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(14, 18);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 16;
|
||||
}
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
if(!usedAPReset) {
|
||||
Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.PIRATE) ? Brawler.IMPROVE_MAX_HP : ThunderBreaker.IMPROVE_MAX_HP);
|
||||
int sLvl = player.getSkillLevel(increaseHP);
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxHP += increaseHP.getEffect(sLvl).getY();
|
||||
}
|
||||
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if (usedAPReset) {
|
||||
MaxHP += 18;
|
||||
} else {
|
||||
MaxHP += Randomizer.rand(16, 20);
|
||||
}
|
||||
} else {
|
||||
MaxHP += 18;
|
||||
}
|
||||
} else if (usedAPReset) {
|
||||
MaxHP += 8;
|
||||
} else {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
MaxHP += Randomizer.rand(8, 12);
|
||||
} else {
|
||||
MaxHP += 10;
|
||||
}
|
||||
}
|
||||
|
||||
return MaxHP;
|
||||
}
|
||||
|
||||
private static int calcMpChange(MapleCharacter player, boolean usedAPReset) {
|
||||
MapleJob job = player.getJob();
|
||||
int MaxMP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10));
|
||||
} else {
|
||||
MaxMP += 2;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 3;
|
||||
}
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
if(!usedAPReset) {
|
||||
Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE);
|
||||
int sLvl = player.getSkillLevel(increaseMP);
|
||||
|
||||
if(sLvl > 0)
|
||||
MaxMP += increaseMP.getEffect(sLvl).getY();
|
||||
}
|
||||
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20));
|
||||
} else {
|
||||
MaxMP += 18;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 18;
|
||||
}
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 10;
|
||||
}
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10));
|
||||
} else {
|
||||
MaxMP += 14;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 14;
|
||||
}
|
||||
} else {
|
||||
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
|
||||
if(!usedAPReset) {
|
||||
MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10));
|
||||
} else {
|
||||
MaxMP += 6;
|
||||
}
|
||||
} else {
|
||||
MaxMP += 6;
|
||||
}
|
||||
}
|
||||
|
||||
return MaxMP;
|
||||
}
|
||||
|
||||
private static int takeHp(MapleJob job) {
|
||||
int MaxHP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
MaxHP += 54;
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
MaxHP += 10;
|
||||
} else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
MaxHP += 20;
|
||||
} else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
MaxHP += 20;
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
MaxHP += 42;
|
||||
} else {
|
||||
MaxHP += 12;
|
||||
}
|
||||
|
||||
return MaxHP;
|
||||
}
|
||||
|
||||
private static int takeMp(MapleJob job) {
|
||||
int MaxMP = 0;
|
||||
|
||||
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
|
||||
MaxMP += 4;
|
||||
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
|
||||
MaxMP += 31;
|
||||
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
|
||||
MaxMP += 12;
|
||||
} else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
|
||||
MaxMP += 12;
|
||||
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
|
||||
MaxMP += 16;
|
||||
} else {
|
||||
MaxMP += 8;
|
||||
}
|
||||
|
||||
return MaxMP;
|
||||
}
|
||||
|
||||
}
|
||||
104
src/main/java/client/processor/stat/AssignSPProcessor.java
Normal file
104
src/main/java/client/processor/stat/AssignSPProcessor.java
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package client.processor.stat;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import constants.game.GameConstants;
|
||||
import constants.skills.Aran;
|
||||
import server.ThreadManager;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RonanLana - synchronization of SP transaction modules
|
||||
*/
|
||||
public class AssignSPProcessor {
|
||||
|
||||
public static boolean canSPAssign(MapleClient c, int skillid) {
|
||||
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return false;
|
||||
}
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
|
||||
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
|
||||
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.");
|
||||
|
||||
c.disconnect(true, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void SPAssignAction(MapleClient c, int skillid) {
|
||||
c.lockClient();
|
||||
try {
|
||||
if (!canSPAssign(c, skillid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter player = c.getPlayer();
|
||||
int remainingSp = player.getRemainingSps()[GameConstants.getSkillBook(skillid/10000)];
|
||||
boolean isBeginnerSkill = false;
|
||||
|
||||
if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
|
||||
int total = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
total += player.getSkillLevel(SkillFactory.getSkill(player.getJobType() * 10000000 + 1000 + i));
|
||||
}
|
||||
remainingSp = Math.min((player.getLevel() - 1), 6) - total;
|
||||
isBeginnerSkill = true;
|
||||
}
|
||||
Skill skill = SkillFactory.getSkill(skillid);
|
||||
int curLevel = player.getSkillLevel(skill);
|
||||
if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) {
|
||||
if (!isBeginnerSkill) {
|
||||
player.gainSp(-1, GameConstants.getSkillBook(skillid/10000), false);
|
||||
} else {
|
||||
player.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
if (skill.getId() == Aran.FULL_SWING) {
|
||||
player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
} else if (skill.getId() == Aran.OVER_SWING) {
|
||||
player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
} else {
|
||||
player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
c.unlockClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user