package net.dev213.ms.handlers.item; import database.data.CharacterPotentialData; import net.dev213.ms.Server; import net.dev213.ms.client.Account; import net.dev213.ms.client.Client; import net.dev213.ms.client.character.BroadcastMsg; import net.dev213.ms.client.character.Char; import net.dev213.ms.client.character.ExtendSP; import net.dev213.ms.client.character.avatar.BeautyAlbum; import net.dev213.ms.client.character.items.*; import net.dev213.ms.client.character.potential.CharacterPotential; import net.dev213.ms.client.character.potential.CharacterPotentialMan; import net.dev213.ms.client.character.quest.Quest; import net.dev213.ms.client.character.skills.Option; import net.dev213.ms.client.character.skills.Skill; import net.dev213.ms.client.character.skills.temp.CharacterTemporaryStat; import net.dev213.ms.client.character.skills.temp.TemporaryStatManager; import net.dev213.ms.connection.InPacket; import net.dev213.ms.connection.packet.*; import net.dev213.ms.constants.*; import net.dev213.ms.enums.*; import net.dev213.ms.handlers.Handler; import net.dev213.ms.handlers.header.InHeader; import net.dev213.ms.life.pet.Pet; import net.dev213.ms.life.pet.PetSkill; import net.dev213.ms.loaders.FieldData; import net.dev213.ms.loaders.ItemData; import net.dev213.ms.loaders.SkillData; import net.dev213.ms.loaders.StringData; import net.dev213.ms.loaders.containerclasses.ItemInfo; import net.dev213.ms.loaders.containerclasses.MakingSkillRecipe; import net.dev213.ms.scripts.ScriptType; import net.dev213.ms.util.Position; import net.dev213.ms.util.Util; import net.dev213.ms.world.World; import net.dev213.ms.world.field.Field; import net.dev213.ms.world.field.Portal; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.text.NumberFormat; import java.util.*; import static net.dev213.ms.enums.ChatType.*; import static net.dev213.ms.enums.EquipBaseStat.iuc; import static net.dev213.ms.enums.EquipBaseStat.tuc; import static net.dev213.ms.enums.InvType.*; import static net.dev213.ms.enums.InventoryOperation.Move; public class ItemHandler { private static final Logger log = LogManager.getLogger(ItemHandler.class); @Handler(op = InHeader.USER_PORTAL_SCROLL_USE_REQUEST) public static void handleUserPortalScrollUseRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); Field field = chr.getField(); if ((field.getFieldLimit() & FieldOption.PortalScrollLimit.getVal()) > 0 || !field.isChannelField()) { chr.chatMessage("You may not use a return scroll in this map."); chr.dispose(); return; } c.verifyTick(inPacket); short slot = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Item item = chr.getConsumeInventory().getItemBySlot(slot); if (item == null || item.getItemId() != itemID || item.getQuantity() < 1 || !chr.isInValidState()) { chr.dispose("You cannot use this return scroll right now."); return; } ItemInfo ii = ItemData.getItemInfoByID(itemID); Field toField; if (itemID != 2030000) { toField = chr.getOrCreateFieldByCurrentInstanceType(ii.getMoveTo()); } else { toField = chr.getOrCreateFieldByCurrentInstanceType(field.getReturnMap()); } Portal portal = toField.getDefaultPortal(); chr.warp(toField, portal); chr.consumeItem(itemID, 1); } @Handler(op = InHeader.USER_STAT_CHANGE_ITEM_CANCEL_REQUEST) public static void handleUserStatChangeItemCancelRequest(Char chr, InPacket inPacket) { TemporaryStatManager tsm = chr.getTemporaryStatManager(); int itemID = inPacket.decodeInt(); tsm.removeStatsBySkill(itemID); tsm.sendResetStatPacket(); } @Handler(op = InHeader.USER_SKILL_RESET_ITEM_USE_REQUEST) public static void handleUserResetSP(Char chr, InPacket inPacket) { if (inPacket != null) { chr.getClient().verifyTick(inPacket); short slot = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Item item = chr.getConsumeInventory().getItemBySlot(slot); if (item == null || itemID != item.getItemId() || itemID / 10 != 250000) { chr.write(WvsContext.resetItemResult(true, chr.getId(), false, true)); return; } chr.consumeItem(item); } short jobID = chr.getJob(); List skills = new ArrayList<>(); List jobs = SkillConstants.getSkillRootFromJob(jobID); ExtendSP extendSP = JobConstants.isExtendSpJob(jobID) ? chr.getAvatarData().getCharacterStat().getExtendSP() : null; int sp = 0; for (int job : jobs) { if (JobConstants.isBeginnerJob((short) job)) { continue; } for (Skill skill : SkillData.getSkillsByJob((short) job)) { Skill curSkill = chr.getSkill(skill.getSkillId()); if (curSkill != null && SkillData.getSkillInfoById(skill.getSkillId()).getHyper() == 0) { sp += curSkill.getCurrentLevel(); curSkill.setCurrentLevel(0); skills.add(curSkill); chr.addSkill(curSkill); } } if (extendSP != null) { byte jobLevel = (byte) JobConstants.getJobLevelDetail((short) job); extendSP.setSpToJobLevel(jobLevel, extendSP.getSpByJobLevel(jobLevel) + sp); sp = 0; } } if (extendSP != null) { chr.write(WvsContext.statChanged(Collections.singletonMap(Stat.sp, extendSP))); } else { chr.setStatAndSendPacket(Stat.sp, chr.getStat(Stat.sp) + sp); } if (!skills.isEmpty()) { chr.write(WvsContext.changeSkillRecordResult(skills, true, false, false, false)); } chr.write(WvsContext.resetItemResult(true, chr.getId(), true, true)); } @Handler(op = InHeader.USER_ABILITY_RESET_ITEM_USE_REQUEST) public static void handleUserResetAP(Char chr, InPacket inPacket) { if (inPacket != null) { chr.getClient().verifyTick(inPacket); short slot = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Item item = chr.getConsumeInventory().getItemBySlot(slot); if (item == null || itemID != item.getItemId() || itemID / 10 != 250100) { chr.write(WvsContext.resetItemResult(true, chr.getId(), false, false)); return; } chr.consumeItem(item); } int ap = chr.getStat(Stat.ap); ap += chr.getStat(Stat.str) - 4; ap += chr.getStat(Stat.dex) - 4; ap += chr.getStat(Stat.inte) - 4; ap += chr.getStat(Stat.luk) - 4; chr.setStat(Stat.str, 4); chr.setStat(Stat.dex, 4); chr.setStat(Stat.inte, 4); chr.setStat(Stat.luk, 4); chr.setStat(Stat.ap, ap); Map stats = Map.of(Stat.str, (short) 4, Stat.dex, (short) 4, Stat.inte, (short) 4, Stat.luk, (short) 4, Stat.ap, (short) ap); chr.write(WvsContext.statChanged(stats)); chr.write(WvsContext.resetItemResult(true, chr.getId(), true, false)); } @Handler(op = InHeader.USER_CONSUME_CASH_ITEM_USE_REQUEST) public static void handleUserConsumeCashItemUseRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); Inventory cashInv = chr.getInventoryByType(InvType.CASH); c.verifyTick(inPacket); short pos = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Item item = cashInv.getItemBySlot(pos); ItemInfo itemInfo = ItemData.getItemInfoByID(itemID); BeautyAlbum album = chr.getBeautyAlbum(); Item cube; int cubeCount; if (item == null || item.getItemId() != itemID) { return; } if (itemID / 10000 == 553) { // Reward items if (itemID % 10000 < 3000) { Object reward; if (itemInfo != null && (reward = itemInfo.getRandomReward()) != null) { if (reward instanceof Item) { chr.addItemToInventory((Item) reward); } else if (reward instanceof Integer && (int) reward != 0) { chr.addMoney((int) reward); } else { chr.chatMessage("Oh, you're unlucky."); // should not happen } } else { chr.chatMessage("Oh, you're unlucky."); } } else if (itemID == 5534000) { // Tim's Secret lab short ePos = (short) inPacket.decodeInt(); InvType invType = ePos < 0 ? EQUIPPED : EQUIP; Equip equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.dispose("Could not find equip."); return; } else if (!ItemConstants.canEquipHavePotential(equip)) { chr.dispose("You cannot use Tim's Secret Lab on this item."); return; } equip.setHiddenOptionBase(ItemGrade.HiddenRare.getVal(), 100); c.write(FieldPacket.showItemUpgradeEffect(chr.getId(), true, false, itemID, equip.getItemId(), false)); equip.updateToChar(chr); } } else if (itemID / 10000 == 539) { // Avatar Megaphones List lineList = new ArrayList<>(); for (int i = 0; i < 4; i++) { String line = inPacket.decodeString(); lineList.add(line); } boolean whisperIcon = inPacket.decodeByte() != 0; World world = c.getWorld(); world.broadcastPacket(WvsContext.setAvatarMegaphone(chr, itemID, lineList, whisperIcon)); } else if (itemID / 10000 == 519) { // Pet Skill Items long sn = inPacket.decodeLong(); PetSkill ps = ItemConstants.getPetSkillFromID(itemID); if (ps == null) { chr.chatMessage(String.format("Unhandled pet skill item %d", itemID)); return; } Item pi = chr.getCashInventory().getItemBySN(sn); if (!(pi instanceof PetItem)) { chr.chatMessage("Could not find that pet."); return; } boolean add = itemID < 5191000; // add property doesn't include the "Slimming Medicine" PetItem petItem = (PetItem) pi; if (add) { petItem.addPetSkill(ps); } else { petItem.removePetSkill(ps); } petItem.updateToChar(chr); } else if (ItemConstants.isMiuMiuMerchant(itemID)) { chr.getScriptManager().openShop(9090000); } else if (ItemConstants.isPortableStorage(itemID)) { chr.getScriptManager().openTrunk(1022005); } else if (itemID / 10000 == 512) { // Weather Items chr.getField().blowWeather(itemID, inPacket.decodeString(), 10, null); int stateChangeItem = itemInfo.getStateChangeItem(); if (stateChangeItem != 0 && !ItemData.getItemInfoByID(stateChangeItem).getSpecStats().isEmpty()) { chr.getField().getChars().forEach(it -> ItemBuffs.giveItemBuffsFromItemID(it, it.getTemporaryStatManager(), stateChangeItem)); } } else { Equip medal = (Equip) chr.getEquippedInventory().getFirstItemByBodyPart(BodyPart.Medal); int medalInt = 0; if (medal != null) { medalInt = (medal.getAnvilId() == 0 ? medal.getItemId() : medal.getAnvilId()); // Check for Anvilled medal } String medalString = (medalInt == 0 ? "" : String.format("<%s> ", StringData.getItemStringById(medalInt))); switch (itemID) { case ItemConstants.HYPER_TELEPORT_ROCK: // Hyper Teleport Rock short type = inPacket.decodeShort(); if (type == 0 || type == 1) { int fieldId = inPacket.decodeInt(); Field field = chr.getOrCreateFieldByCurrentInstanceType(fieldId); if (field == null || (chr.getField().getFieldLimit() & FieldOption.TeleportItemLimit.getVal()) > 0 || !FieldData.getWorldMapFields().contains(fieldId)) { chr.chatMessage("You may not warp to that map, as you cannot teleport from your current map."); chr.dispose(); return; } chr.setInstance(null); chr.warp(field); } else { String targetName = inPacket.decodeString(); int worldID = chr.getClient().getChannelInstance().getWorldId().getVal(); World world = Server.getInstance().getWorldById(worldID); Char targetChr = world.getCharByName(targetName); // Target doesn't exist or target char is not a regular player if (targetChr == null || targetChr.getAccount().getAccountType().getVal() >= 1) { chr.chatMessage(String.format("%s is not online.", targetName)); chr.dispose(); return; } Position targetPosition = targetChr.getPosition(); Field targetField = targetChr.getField(); if (targetField == null || (targetField.getFieldLimit() & FieldOption.TeleportItemLimit.getVal()) > 0) { chr.chatMessage("You may not warp to that map, as the targeted map cannot be teleported to."); chr.dispose(); return; } // Target is in an instanced Map if (targetChr.getInstance() != null) { chr.chatMessage(String.format("cannot find %s", targetName)); // Change channels & warp & teleport } else if (targetChr.getClient().getChannel() != c.getChannel()) { chr.setInstance(null); chr.chatMessage(String.format("cannot find %s on this channel", targetName)); //chr.changeChannelAndWarp(targetChr.getClient().getChannel(), fieldId); // Makes you warp to player without being in the same CH return; // warp & teleport } else if (targetChr.getFieldID() != chr.getFieldID()) { chr.setInstance(null); chr.warp(targetField); chr.write(FieldPacket.teleport(targetPosition, chr)); // teleport } else { chr.write(FieldPacket.teleport(targetPosition, chr)); } } break; case 5170000: int petID = inPacket.decodeInt(); inPacket.decodeInt(); //?? String petName1 = inPacket.decodeString(); Pet petid = chr.getPetById(petID); if (petName1.length() > 13) { chr.chatMessage("no"); chr.dispose(); return; } if (petid != null) { petid.setName(petName1); petid.getItem().setName(petName1); int idx = petid.getIdx(); chr.write(UserLocal.petNameChange(chr, idx, petName1)); } break; case ItemConstants.OCCULT_CUBE: case ItemConstants.RED_CUBE: // Red Cube case ItemConstants.BLACK_CUBE: // Black cube cube = chr.getCashInventory().getItemByItemID(itemID); cubeCount = cube.getQuantity(); cubeCount--; short ePos = (short) inPacket.decodeInt(); InvType invType = ePos < 0 ? EQUIPPED : EQUIP; Equip equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.chatMessage(SystemNotice, "Could not find equip."); chr.dispose(); return; } else if (equip.getBaseGrade() < ItemGrade.Rare.getVal()) { String msg = String.format("Character %d tried to use cube (id %d) an equip without a potential (id %d)", chr.getId(), itemID, equip.getItemId()); chr.getOffenseManager().addOffense(msg); chr.dispose(); return; } else if (itemID == ItemConstants.OCCULT_CUBE && equip.getBaseGrade() > ItemGrade.Epic.getVal()) { chr.chatMessage(SystemNotice, "You may only use this on Rare or Epic Items!"); chr.dispose(); return; } short hiddenValue = ItemGrade.getHiddenGradeByVal(equip.getBaseGrade()).getVal(); int tierUpChance = ItemConstants.getTierUpChance(itemID, hiddenValue); boolean tierUp = tierUpChance > 0 && Util.succeedProp(tierUpChance); if (tierUp && (itemID != ItemConstants.OCCULT_CUBE || equip.getBaseGrade() != ItemGrade.Epic.getVal())) { hiddenValue++; } if (itemID == ItemConstants.RED_CUBE) { equip.setHiddenOptionBase(hiddenValue, 0); equip.releaseOptions(false); chr.getField().broadcastPacket(UserPacket.showItemMemorialEffect(chr.getId(), true, itemID, ePos, pos)); c.write(FieldPacket.redCubeResult(chr.getId(), tierUp, itemID, ePos, equip, cubeCount)); // c.write(FieldPacket.showItemReleaseEffect(chr.getId(), ePos, false)); equip.updateToChar(chr); if (invType == EQUIPPED) { chr.recalcStats(equip.getBaseStatFlag()); } } else { if (chr.getMemorialCubeInfo() == null) { chr.setMemorialCubeInfo(new MemorialCubeInfo(equip.deepCopy(), itemID)); } Equip newEquip = chr.getMemorialCubeInfo().getEquip(); newEquip.setHiddenOptionBase(hiddenValue, 0); newEquip.releaseOptions(false); chr.getField().broadcastPacket(UserPacket.showItemMemorialEffect(chr.getId(), true, itemID, ePos, pos)); c.write(WvsContext.blackCubeResult(equip, chr.getMemorialCubeInfo(), cubeCount)); } break; case ItemConstants.VIOLET_CUBE: // Violet cube ePos = (short) inPacket.decodeInt(); invType = ePos < 0 ? EQUIPPED : EQUIP; equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.chatMessage(SystemNotice, "Could not find equip."); chr.dispose(); return; } long number = equip.getId(); Equip copy = equip.deepCopy(); if (copy.getBaseGrade() < ItemGrade.Rare.getVal()) { String msg = String.format("Character %d tried to use cube (id %d) an equip without a potential (id %d)", chr.getId(), itemID, equip.getItemId()); chr.getOffenseManager().addOffense(msg); chr.dispose(); return; } else if (chr.getMoney() < 36000) { // TODO: correct cost by level c.write(WvsContext.broadcastMsg(BroadcastMsg.popUpMessage("You do not have enough mesos."))); c.write(CUIHandler.violetCubeResult(0, 1, 0, Collections.emptyList())); return; } byte line = (byte) Arrays.stream(copy.getOptionBase()).filter(option -> option > 0).count(); if (line <= 1) { chr.chatMessage(SystemNotice, "You cannot use Violet Cube on this item."); c.write(CUIHandler.violetCubeResult(0, 1, line, Collections.emptyList())); return; } short optionGrade = ItemGrade.getHiddenGradeByVal(copy.getBaseGrade()).getVal(); tierUpChance = ItemConstants.getTierUpChance(itemID, optionGrade); if (tierUpChance > 0 && Util.succeedProp(tierUpChance)) { optionGrade++; } copy.setItemState(optionGrade); List options = new ArrayList<>(line * 2); for (byte i = 0; i < line * 2; i++) { options.add(copy.getRandomOption(false, i % 3)); // ensure 2 of 4/6 options are higher rank } Quest quest = chr.getQuestManager().getOrCreateQuestById(QuestConstants.VIOLET_CUBE_INFO); quest.setProperty("o", options.toString().replace("[", "").replace("]", "").replace(" ", "")); quest.setProperty("n", String.valueOf(number)); quest.setProperty("p", ePos); quest.setProperty("c", line); quest.setProperty("i", copy.getItemId()); quest.setProperty("og", optionGrade); chr.write(WvsContext.questRecordExMessage(quest)); chr.deductMoney(36000); // TODO: correct cost by level c.write(FieldPacket.showItemUnReleaseEffect(chr.getId(), true, itemID, 0, copy.getItemId())); c.write(CUIHandler.violetCubeResult(0, 0, line, options)); chr.chatMessage(options.toString()); break; case ItemConstants.BONUS_POT_CUBE: // Bonus Potential Cube case ItemConstants.SPECIAL_BONUS_POT_CUBE: // [Special] Bonus Potential Cube case ItemConstants.WHITE_BONUS_POT_CUBE: // White Bonus Potential Cube cube = chr.getCashInventory().getItemByItemID(itemID); cubeCount = cube.getQuantity(); cubeCount--; if (c.getWorld().isReboot()) { chr.getOffenseManager().addOffense(String.format("Character %d attempted to use a bonus potential cube in reboot world.", chr.getId())); chr.dispose(); return; } ePos = (short) inPacket.decodeInt(); invType = ePos < 0 ? EQUIPPED : EQUIP; equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.chatMessage(SystemNotice, "Could not find equip."); chr.dispose(); return; } else if (equip.getBonusGrade() < ItemGrade.Rare.getVal()) { chr.getOffenseManager().addOffense(String.format("Character %d tried to use cube (id %d) an equip without a potential (id %d)", chr.getId(), itemID, equip.getItemId())); chr.dispose(); return; } hiddenValue = ItemGrade.getHiddenGradeByVal(equip.getBonusGrade()).getVal(); tierUpChance = ItemConstants.getTierUpChance(itemID, hiddenValue); tierUp = tierUpChance > 0 && Util.succeedProp(tierUpChance); if (tierUp) { hiddenValue++; } if (itemID != ItemConstants.WHITE_BONUS_POT_CUBE) { equip.setHiddenOptionBonus(hiddenValue, 0); equip.releaseOptions(true); chr.getField().broadcastPacket(UserPacket.showItemMemorialEffect(chr.getId(), true, itemID, ePos, pos)); c.write(FieldPacket.bonusCubeResult(chr.getId(), tierUp, itemID, ePos, equip, cubeCount)); // c.write(FieldPacket.showItemReleaseEffect(chr.getId(), ePos, true)); equip.updateToChar(chr); if (invType == EQUIPPED) { chr.recalcStats(equip.getBaseStatFlag()); } } else { if (chr.getMemorialCubeInfo() == null) { chr.setMemorialCubeInfo(new MemorialCubeInfo(equip.deepCopy(), itemID)); } Equip newEquip = chr.getMemorialCubeInfo().getEquip(); newEquip.setHiddenOptionBonus(hiddenValue, 0); newEquip.releaseOptions(true); chr.getField().broadcastPacket(UserPacket.showItemMemorialEffect(chr.getId(), true, itemID, ePos, pos)); c.write(WvsContext.whiteCubeResult(equip, chr.getMemorialCubeInfo(), cubeCount)); } break; case 5520001: // Platinum Scissors of Karma inPacket.decodeInt(); // 1 ePos = (short) inPacket.decodeInt(); equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (equip == null) { chr.chatMessage("Could not find equip."); chr.dispose(); return; } else if (equip.hasAttribute(EquipAttribute.Locked) || !equip.hasAttribute(EquipAttribute.Untradable) || equip.hasAttribute(EquipAttribute.UntradableAfterTransaction) || equip.getCuttable() == 0) { chr.chatMessage("You cannot use Scissors of Karma on this equip."); chr.dispose(); return; } equip.addAttribute(EquipAttribute.UntradableAfterTransaction); equip.setTradeBlock(false); equip.setEquipTradeBlock(true); //equip.setCuttable((short) 0); equip.updateToChar(chr); break; case 5750001: // Nebulite Diffuser ePos = inPacket.decodeShort(); equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (equip == null || equip.getSocket(0) == 0 || equip.getSocket(0) == ItemConstants.EMPTY_SOCKET_ID) { chr.chatMessage("That item currently does not have an active socket."); chr.dispose(); return; } equip.setSocket(0, ItemConstants.EMPTY_SOCKET_ID); equip.updateToChar(chr); break; case 5072000: // Super Megaphone String text = inPacket.decodeString(); boolean whisperIcon = inPacket.decodeByte() != 0; World world = chr.getClient().getWorld(); BroadcastMsg smega = BroadcastMsg.megaphone( String.format("%s%s : %s", medalString, chr.getName(), text), (byte) chr.getClient().getChannelInstance().getChannelId(), whisperIcon, chr); world.broadcastPacket(WvsContext.broadcastMsg(smega)); break; case 5076000: // Item Megaphone text = inPacket.decodeString(); whisperIcon = inPacket.decodeByte() != 0; boolean eqpSelected = inPacket.decodeByte() != 0; invType = EQUIP; int itemPosition = 0; if (eqpSelected) { invType = InvType.getInvTypeByVal(inPacket.decodeInt()); itemPosition = inPacket.decodeInt(); if (invType == EQUIP && itemPosition < 0) { invType = EQUIPPED; } } Item broadcastedItem = chr.getInventoryByType(invType).getItemBySlot(itemPosition); world = chr.getClient().getWorld(); smega = BroadcastMsg.itemMegaphone(String.format("%s%s : %s", medalString, chr.getName(), text), (byte) chr.getClient().getChannelInstance().getChannelId(), whisperIcon, eqpSelected, broadcastedItem, chr); world.broadcastPacket(WvsContext.broadcastMsg(smega)); break; case 5077000: // Triple Megaphone byte stringListSize = inPacket.decodeByte(); List stringList = new ArrayList<>(); for (int i = 0; i < stringListSize; i++) { stringList.add(String.format("%s%s : %s", medalString, chr.getName(), inPacket.decodeString())); } whisperIcon = inPacket.decodeByte() != 0; world = chr.getClient().getWorld(); smega = BroadcastMsg.tripleMegaphone(stringList, (byte) chr.getClient().getChannelInstance().getChannelId(), whisperIcon, chr); world.broadcastPacket(WvsContext.broadcastMsg(smega)); break; case 5062400: // Fusion anvil case 5062402: // Medal Fusion anvil case 5062405: // Fusion anvil int appearancePos = inPacket.decodeInt(); int functionPos = inPacket.decodeInt(); Inventory inv = chr.getEquipInventory(); Equip appearance = (Equip) inv.getItemBySlot(appearancePos); Equip function = (Equip) inv.getItemBySlot(functionPos); if (appearance == null || function == null) { chr.dispose("Could not find equip."); return; } else if (appearance.getItemId() / 10000 != function.getItemId() / 10000) { chr.dispose("Fusion only works on equipment of the same type. Please check the item you want to Fuse."); return; } else if (itemID == 5062402 && !ItemConstants.isMedal(appearance.getItemId())) { chr.dispose("Medal Fusion Anvil only works on medals."); return; } else if (itemID != 5062402 && ItemConstants.isMedal(appearance.getItemId())) { chr.dispose("Fusion Anvil doesn't work on medals."); return; } if (appearance.getItemId() / 10000 == function.getItemId() / 10000) { function.setOption(6, appearance.getItemId() % 10000, false); } function.updateToChar(chr); break; //These are semi buggy case 5155000: //Elf to Human | anyone to Elf if (chr.getCarta() == 1) { chr.setCarta(0); } else { chr.setCarta(1); } break; case 5155004: //Illium to Human | anyone to Illium if (chr.getCarta() == 2) { chr.setCarta(0); } else { chr.setCarta(2); } break; case 5155005: //Ark to Human | anyone to Ark if (chr.getCarta() == 3) { chr.setCarta(0); } else { chr.setCarta(3); } break; case 5068300: //Random Pet Box int[] pets = new int[]{ 5002106, 5002107, 5002108, 5000921, 5000922, 5000923, 5002076, 5002077, 5002078, 5002057, 5002058, 5002059, 5000954, 5000955, 5000956, 5002048, 5002049, 5002050, 5002045, 5002046, 5002047, 5000960, 5000961, 5000962, 5002066, 5002067, 5002068, 5002030, 5002031, 5002032, 5002040, 5002039, 5002036, 5002037, 5002038, 5002033, 5002034, 5002035, 5002028, 5002029, 5002021, 5002022, 5002023, 5002017, 5002011, 5002012, 5002013, 5002003, 5002004, 5002005, 5000997, 5000998, 5000999, 5000979, 5000980, 5000981, 5000977, 5000794, 5000973, 5000972, 5000971, 5000970, 5000964, 5000965, 5000966, 5000967, 5000968, 5000969, 5000909, 5000910, 5000911, 5000942, 5000943, 5000944, 5000939, 5000933, 5000934, 5000935, 5000915, 5000916, 5000917, 5000912, 5000913, 5000914, 5000906, 5000907, 5000908, 5000900, 5000901, 5000902, 5000846, 5000847, 5000848, 5000836, 5000835, 5000822, 5000823, 5000824, 5000812, 5000813, 5000814, 5000798, 5000793, 5000794, 5000796, 5000762, 5000763, 5000764, 5000696, 5000633, 5000634, 5000635, 5000565, 5000566, 5000567, 5000460, 5000461, 5000462, 5000677, 5000678, 5000443, 5000444, 5000445, 5000647, 5000648, 5000649, 5000636, 5000637, 5000638, 5000637, 5000638, 5000536, 5000537, 5000538, 5000456, 5000457, 5000458, 5000499, 5000405, 5000406, 5000407, 5000402, 5000403, 5000404, 5000385, 5000386, 5000387, 5000368, 5000341, 5000316 }; Random pet = new Random(); int randomIndex = pet.nextInt(pets.length); int getRandomPet = pets[randomIndex]; String petName = StringData.getItemStringById(getRandomPet); if (chr.canHold(getRandomPet, 1)) { String worldTxt = (chr.getName() + " just got a cute " + petName + " from the Wisp's Wondrous Wonderberry"); chr.getWorld().broadcastPacket(UserLocal.chatMsg(MiracleTime, worldTxt)); chr.addItemToInventory(getRandomPet, 1); } else { chr.chatMessage("Make room in your cash Inventory!"); } break; case 5222058: int[] chairs = new int[]{3018427, 3018425, 3018424, 3018416, 3018390, 3018389, 3018370, 3018369, 3018365, 3018358}; Random chair = new Random(); int randomcIndex = chair.nextInt(chairs.length); int getRandomChair = chairs[randomcIndex]; String chairName = StringData.getItemStringById(getRandomChair); if (chr.canHold(getRandomChair, 1)) { String worldTxt = (chr.getName() + " just got a " + chairName + " from the Chair Box"); chr.getWorld().broadcastPacket(UserLocal.chatMsg(MiracleTime, worldTxt)); chr.addItemToInventory(getRandomChair, 1); } else { chr.chatMessage("Make room in your SetUp Inventory!"); } break; case 5062800: // Miracle Circulator case 5062801: CharacterPotentialMan cpm = chr.getPotentialMan(); if (cpm.getGrade() == 0) { chr.dispose("You cannot use Miracle Circulator in your current state."); return; } byte grade = cpm.getGrade(); // update grades if (grade < CharPotGrade.Legendary.ordinal() && Util.succeedProp(GameConstants.BASE_CHAR_POT_UP_RATE)) { grade++; } MiracleCirculatorInfo mci = new MiracleCirculatorInfo(itemID, item.getId()); List potentials = CharacterPotentialMan.generateRandomPotential(3, grade, false, null); potentials.sort(Comparator.comparingInt(CharacterPotentialData::getKey)); for (CharacterPotential cp : potentials) { mci.getPotentials().add(cp); chr.chatMessage("Key:" + cp.getKey() + " Grade:" + cp.getGrade() + " SkillID:" + cp.getSkillID() + "(" + StringData.getSkillStringById(cp.getSkillID()).getName() + ") SLV:" + cp.getSlv()); } chr.setMiracleCirculatorInfo(mci); c.write(WvsContext.miracleCirculatorResult(mci, pos)); break; case 5570000: inPacket.decodeInt(); // use hammer? useless though ePos = (short) inPacket.decodeInt(); // equip slot invType = ePos < 0 ? EQUIPPED : EQUIP; equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.write(WvsContext.viciousHammerItemUpgradeResult(192, 1, 0)); return; } short maxHammers = ItemConstants.MAX_HAMMER_SLOTS; Equip defaultEquip = ItemData.getEquipById(equip.getItemId()); if (defaultEquip.isHasIUCMax()) { maxHammers = defaultEquip.getIUCMax(); } if (equip.getIuc() >= maxHammers) { chr.write(WvsContext.viciousHammerItemUpgradeResult(192, 2, 0)); chr.getOffenseManager().addOffense(String.format("Character %d tried to use Vicious's Hammer an invalid equip (id %d)", chr.getId(), equip.getItemId())); return; } if (!ItemConstants.canEquipGoldHammer(equip)) { chr.write(WvsContext.viciousHammerItemUpgradeResult(192, 3, 0)); chr.getOffenseManager().addOffense(String.format("Character %d tried to use Vicious' Hammer on an invalid equip (id %d)", chr.getId(), equip.getItemId())); return; } equip.addStat(iuc, 1); // +1 hammer used equip.addStat(tuc, 1); // +1 upgrades available equip.updateToChar(chr); chr.write(WvsContext.viciousHammerItemUpgradeResult(190, 0, equip.getIuc())); break; case 5050100: // AP Reset Scroll handleUserResetAP(chr, null); break; case 5051001: // SP Reset Scroll handleUserResetSP(chr, null); break; case 5064000: // Shielding Ward (reg 12 stars) case 5064003: // Superior Shielding Ward (reg 7 stars) case 5064100: // Shield Scroll case 5064300: // Guardian Scroll case 5064400: // Return Scroll case 5068100: // Pet Shield Scroll ePos = inPacket.decodeShort(); inPacket.decodeByte(); invType = ePos < 0 ? EQUIPPED : EQUIP; equip = (Equip) chr.getInventoryByType(invType).getItemBySlot(ePos); if (equip == null) { chr.dispose("Could not find equip."); return; } else if (itemID == 5064000 && equip.getChuc() >= 12 // TODO: superior item check || itemID == 5064003 && equip.getChuc() >= 7 || itemID == 5068100 && equip.getItemId() / 10000 != EquipPrefix.PetWear.getVal()) { chr.dispose("You cannot use this scroll on this item."); return; } switch (itemID % 10000 / 100) { case 40 -> equip.addAttribute(EquipAttribute.ProtectionScroll); case 41, 81 -> equip.addAttribute(EquipAttribute.UpgradeCountProtection); case 43 -> equip.addAttribute(EquipAttribute.ScrollProtection); case 44 -> equip.addAttribute(EquipAttribute.ReturnScroll); // TODO: add actual effects on server side } chr.chatMessage("The scroll effect is not implemented yet."); equip.updateToChar(chr); break; case 5553000: if (album.getHairSlot() < 50) { album.setHairSlot(album.getHairSlot() + 1); chr.write(UserLocal.beautyAlbumActions((byte) 0x5, 30000, album.getHairSlot(), album)); } break; case 5552000: if (album.getFaceSlot() < 50) { album.setFaceSlot(album.getFaceSlot() + 1); chr.write(UserLocal.beautyAlbumActions((byte) 0x5, 20000, album.getFaceSlot(), album)); } break; default: chr.chatMessage(Mob, String.format("Cash item %d is not implemented, notify Taiga pls.", itemID)); return; } } if (itemID != 5040004 && itemID / 10000 != 545) { // TP Rocks and Portable Stores / Storages chr.consumeItem(item); } chr.dispose(); } @Handler(op = InHeader.USER_ARCANE_SYMBOL_MERGE_REQUEST) public static void handleUserArcaneSymbolMergeRequest(Char chr, InPacket inPacket) { int type = inPacket.decodeInt(); int fromPos = inPacket.decodeInt(); switch (type) { case 0: // merge Equip fromEq = (Equip) chr.getEquipInventory().getItemBySlot(fromPos); Equip toEq = (Equip) chr.getEquippedInventory().getItemByItemID(fromEq == null ? 0 : fromEq.getItemId()); if (fromEq == null || toEq == null || fromEq.getItemId() != toEq.getItemId() || !ItemConstants.isArcaneSymbol(toEq.getItemId())) { chr.chatMessage("Could not find one of the symbols."); return; } if (toEq.getSymbolExp() >= ItemConstants.getRequiredSymbolExp(toEq.getSymbolLevel())) { chr.chatMessage("Your symbol already is at max exp."); return; } chr.consumeItem(fromEq); toEq.setSymbolExp(Math.min(toEq.getSymbolExp() + fromEq.getSymbolExp(), ItemConstants.getRequiredSymbolExp(toEq.getSymbolLevel()))); toEq.updateToChar(chr); break; case 1: // enhance Equip symbol = (Equip) chr.getEquippedInventory().getItemBySlot(fromPos); int reqSymbolExp = ItemConstants.getRequiredSymbolExp(symbol.getLevel()); if (symbol == null || !ItemConstants.isArcaneSymbol(symbol.getItemId()) || symbol.getSymbolLevel() >= ItemConstants.MAX_ARCANE_SYMBOL_LEVEL || symbol.getSymbolExp() < reqSymbolExp) { chr.chatMessage("Could not find symbol."); return; } long cost = ItemConstants.getSymbolMoneyReqByLevel(symbol.getSymbolLevel()); if (cost > chr.getMoney()) { chr.chatMessage("You do not have enough mesos to level up your symbol."); return; } chr.deductMoney(cost); symbol.setSymbolLevel((short) (symbol.getSymbolLevel() + 1)); symbol.addSymbolExp(-reqSymbolExp); symbol.initSymbolStats(symbol.getSymbolLevel(), chr.getJob()); symbol.updateToChar(chr); chr.recalcStats(symbol.getBaseStatFlag()); break; case 2: // mass merge int itemId = fromPos; toEq = (Equip) chr.getEquippedInventory().getItemByItemID(itemId); if (!(toEq instanceof Equip) || !ItemConstants.isArcaneSymbol(itemId)) { chr.chatMessage("Could not find an arcane symbol to transfer to."); return; } if (toEq.hasSymbolExpForLevelUp()) { chr.chatMessage("First level your symbol before trying to add more symbols onto it."); return; } Set matchingSymbols = new HashSet<>(); for (Item item : chr.getEquipInventory().getItems()) { if (item.getItemId() == toEq.getItemId()) { matchingSymbols.add((Equip) item); } } for (Equip eqSymbol : matchingSymbols) { chr.consumeItem(eqSymbol); toEq.addSymbolExp(eqSymbol.getTotalSymbolExp()); if (toEq.hasSymbolExpForLevelUp()) { break; } } toEq.updateToChar(chr); break; } } @Handler(op = InHeader.USER_STAT_CHANGE_ITEM_USE_REQUEST) public static void handleUserStatChangeItemUseRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); Field field = chr.getField(); if ((field.getFieldLimit() & FieldOption.StatChangeItemConsumeLimit.getVal()) > 0) { chr.dispose(); return; } TemporaryStatManager tsm = chr.getTemporaryStatManager(); c.verifyTick(inPacket); short slot = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Item item = chr.getConsumeInventory().getItemBySlot(slot); if (item == null || item.getItemId() != itemID || !chr.isInValidState()) { chr.dispose(); return; } chr.useStatChangeItem(item, true); if (field.getConsumeItemCoolTime() > 0) { chr.write(UserLocal.consumeItemCooltime()); } } @Handler(op = InHeader.USER_SCRIPT_ITEM_USE_REQUEST) public static void handleUserScriptItemUseRequest(Client c, InPacket inPacket) { c.verifyTick(inPacket); short slot = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); int quant = inPacket.decodeInt(); Char chr = c.getChr(); Item item = chr.getConsumeInventory().getItemBySlot(slot); if (item == null || item.getItemId() != itemID) { item = chr.getCashInventory().getItemBySlot(slot); } if (item == null || item.getItemId() != itemID || quant < 0 || !chr.isInValidState()) { chr.dispose(); return; } String script = String.valueOf(itemID); ItemInfo ii = ItemData.getItemInfoByID(itemID); if (ii.getScript() != null && !"".equals(ii.getScript())) { script = ii.getScript(); } if (item.getItemId() == ItemConstants.GAGAGUCCI) { int qid = QuestConstants.PVAC_DATA; if (chr.getRecordFromQuestEx(qid, "vac") == 0) { chr.setQuestRecordEx(qid, "vac", 1); chr.chatMessage("Pvac has been enabled"); } else { chr.setQuestRecordEx(qid, "vac", 0); chr.chatMessage("Pvac has been disabled"); } chr.dispose(); return; } chr.getScriptManager().startScript(itemID, script, ScriptType.Item); chr.dispose(); } @Handler(op = InHeader.USER_EQUIPMENT_ENCHANT_WITH_SINGLE_UI_REQUEST) public static void handleUserEquipmentEnchantWithSingleUIRequest(Client c, InPacket inPacket) { byte equipmentEnchantType = inPacket.decodeByte(); Char chr = c.getChr(); EquipmentEnchantType eeType = EquipmentEnchantType.getByVal(equipmentEnchantType); if (eeType == null) { log.error(String.format("Unknown enchant UI request %d", equipmentEnchantType)); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } switch (eeType) { case ScrollUpgradeRequest: c.verifyTick(inPacket); short pos = inPacket.decodeShort(); int scrollID = inPacket.decodeInt(); Inventory inv = pos < 0 ? chr.getEquippedInventory() : chr.getEquipInventory(); pos = (short) Math.abs(pos); Equip equip = (Equip) inv.getItemBySlot(pos); if (equip == null || equip.hasSpecialAttribute(EquipSpecialAttribute.Vestige)) { chr.getOffenseManager().addOffense(String.format("Character %d tried to enchant a non-scrollable equip (pos %d, itemid %d).", chr.getId(), pos, equip == null ? 0 : equip.getItemId())); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } Equip prevEquip = equip.deepCopy(); List suis = ItemConstants.getScrollUpgradeInfosByEquip(equip); if (scrollID < 0 || scrollID >= suis.size()) { chr.getOffenseManager().addOffense(String.format("Characer %d tried to spell trace scroll with an invalid scoll ID (%d, " + "itemID %d).", chr.getId(), scrollID, equip.getItemId())); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } ScrollUpgradeInfo sui = suis.get(scrollID); if (equip.getBaseStat(tuc) <= 0 && sui.getType() == SpellTraceScrollType.Normal) { chr.getOffenseManager().addOffense(String.format("Character %d tried to enchant a non-scrollable equip (pos %d, itemid %d).", chr.getId(), pos, equip.getItemId())); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } chr.consumeItem(ItemConstants.SPELL_TRACE_ID, sui.getCost()); boolean success = sui.applyTo(equip); equip.recalcEnchantmentStats(); String desc = success ? "Your item has been upgraded." : "Your upgrade has failed."; chr.write(FieldPacket.showScrollUpgradeResult(false, success ? 1 : 0, desc, prevEquip, equip)); equip.updateToChar(chr); if (pos < 0) { chr.recalcStats(equip.getBaseStatFlag()); } suis = ItemConstants.getScrollUpgradeInfosByEquip(equip); c.write(FieldPacket.scrollUpgradeDisplay(false, suis)); break; case HyperUpgradeResult: c.verifyTick(inPacket); int eqpPos = inPacket.decodeShort(); boolean extraChanceFromMiniGame = inPacket.decodeByte() != 0; equip = (Equip) chr.getEquipInventory().getItemBySlot(eqpPos); if (extraChanceFromMiniGame) { inPacket.decodeInt(); } inPacket.decodeInt(); inPacket.decodeInt(); boolean safeGuard = inPacket.decodeByte() != 0; boolean equippedInv = eqpPos < 0; inv = equippedInv ? chr.getEquippedInventory() : chr.getEquipInventory(); equip = (Equip) inv.getItemBySlot(Math.abs(eqpPos)); if (equip == null) { chr.chatMessage("Could not find the given equip."); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } if (!ItemConstants.isUpgradable(equip.getItemId()) || (equip.getBaseStat(tuc) != 0 && !c.getWorld().isReboot()) || chr.getEquipInventory().getEmptySlots() == 0 || equip.getChuc() >= GameConstants.getMaxStars(equip) || equip.hasSpecialAttribute(EquipSpecialAttribute.Vestige)) { chr.chatMessage("Equipment cannot be enhanced."); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } long cost = GameConstants.getEnchantmentMesoCost(equip.getrLevel() + equip.getiIncReq(), equip.getChuc(), equip.isSuperiorEqp()); if (chr.getMoney() < cost) { chr.chatMessage("Mesos required: " + NumberFormat.getNumberInstance(Locale.US).format(cost)); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } Equip oldEquip = equip.deepCopy(); int successProp = GameConstants.getEnchantmentSuccessRate(equip); if (extraChanceFromMiniGame) { successProp *= 1.045; } int destroyProp = safeGuard && equip.canSafeguardHyperUpgrade() ? 0 : GameConstants.getEnchantmentDestroyRate(equip); if (equippedInv && destroyProp > 0 && chr.getEquipInventory().getEmptySlots() == 0) { c.write(WvsContext.broadcastMsg(BroadcastMsg.popUpMessage("You do not have enough space in your " + "equip inventory in case your item gets destroyed."))); return; } success = Util.succeedProp(successProp, 1000); boolean boom = false; boolean canDegrade = equip.isSuperiorEqp() ? equip.getChuc() > 0 : equip.getChuc() > 5 && equip.getChuc() % 5 != 0; if (success) { equip.setChuc((short) (equip.getChuc() + 1)); equip.setDropStreak(0); } else if (Util.succeedProp(destroyProp, 1000)) { equip.setChuc((short) 0); equip.makeVestige(); boom = true; if (equippedInv) { chr.unequip(equip); equip.setBagIndex(chr.getEquipInventory().getFirstOpenSlot()); equip.updateToChar(chr); c.write(WvsContext.inventoryOperation(true, false, Move, (short) eqpPos, (short) equip.getBagIndex(), 0, equip)); } if (!equip.isSuperiorEqp()) { equip.setChuc((short) Math.min(12, equip.getChuc())); } else { equip.setChuc((short) 0); } } else if (canDegrade) { equip.setChuc((short) (equip.getChuc() - 1)); equip.setDropStreak(equip.getDropStreak() + 1); } chr.deductMoney(cost); equip.recalcEnchantmentStats(); oldEquip.recalcEnchantmentStats(); equip.updateToChar(chr); if (equippedInv) { chr.recalcStats(equip.getBaseStatFlag()); } c.write(FieldPacket.showUpgradeResult(oldEquip, equip, success, boom, canDegrade)); chr.dispose(); break; case TransmissionResult: c.verifyTick(inPacket); short toPos = inPacket.decodeShort(); short fromPos = inPacket.decodeShort(); Equip fromEq = (Equip) chr.getEquipInventory().getItemBySlot(fromPos); Equip toEq = (Equip) chr.getEquipInventory().getItemBySlot(toPos); if (fromEq == null || toEq == null || fromEq.getItemId() != toEq.getItemId() || !fromEq.hasSpecialAttribute(EquipSpecialAttribute.Vestige)) { log.error(String.format("Equip transmission failed: from = %s, to = %s", fromEq, toEq)); c.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } fromEq.removeVestige(); fromEq.setChuc((short) 0); chr.consumeItem(toEq); fromEq.updateToChar(chr); c.write(FieldPacket.showTranmissionResult(fromEq, toEq)); break; case ScrollUpgradeDisplay: int ePos = inPacket.decodeInt(); inv = ePos < 0 ? chr.getEquippedInventory() : chr.getEquipInventory(); ePos = Math.abs(ePos); equip = (Equip) inv.getItemBySlot(ePos); if (c.getWorld().isReboot()) { chr.getOffenseManager().addOffense(String.format("Character %d attempted to scroll in reboot world (pos %d, itemid %d).", chr.getId(), ePos, equip == null ? 0 : equip.getItemId())); chr.dispose(); return; } if (equip == null || equip.hasSpecialAttribute(EquipSpecialAttribute.Vestige) || !ItemConstants.isUpgradable(equip.getItemId())) { chr.getOffenseManager().addOffense(String.format("Character %d tried to scroll a non-scrollable equip (pos %d, itemid %d).", chr.getId(), ePos, equip == null ? 0 : equip.getItemId())); chr.dispose(); return; } suis = ItemConstants.getScrollUpgradeInfosByEquip(equip); c.write(FieldPacket.scrollUpgradeDisplay(false, suis)); break; /*case ScrollTimerEffective: break;*/ case HyperUpgradeDisplay: ePos = inPacket.decodeInt(); safeGuard = inPacket.decodeByte() != 0; inv = ePos < 0 ? chr.getEquippedInventory() : chr.getEquipInventory(); ePos = Math.abs(ePos); equip = (Equip) inv.getItemBySlot(ePos); if (equip == null || equip.hasSpecialAttribute(EquipSpecialAttribute.Vestige) || !ItemConstants.isUpgradable(equip.getItemId())) { chr.getOffenseManager().addOffense(String.format("Character %d tried to enchant a non-enchantable equip (pos %d, itemid %d).", chr.getId(), ePos, equip == null ? 0 : equip.getItemId())); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); return; } cost = GameConstants.getEnchantmentMesoCost(equip.getrLevel() + equip.getiIncReq(), equip.getChuc(), equip.isSuperiorEqp()); destroyProp = GameConstants.getEnchantmentDestroyRate(equip); if (safeGuard && equip.canSafeguardHyperUpgrade()) { cost *= 2; } c.write(FieldPacket.hyperUpgradeDisplay(equip, equip.isSuperiorEqp() ? equip.getChuc() > 0 : equip.getChuc() > 5 && equip.getChuc() % 5 != 0, cost, 0, 0, GameConstants.getEnchantmentSuccessRate(equip), 0, destroyProp, 0, equip.getDropStreak() >= 2)); break; case MiniGameDisplay: c.write(FieldPacket.miniGameDisplay(eeType)); break; //case ShowScrollUpgradeResult: case ScrollTimerEffective: case ShowHyperUpgradeResult: break; /* case ShowScrollVestigeCompensationResult: case ShowTransmissionResult: case ShowUnknownFailResult: break;*/ default: log.debug("Unhandled Equipment Enchant Type: " + eeType); chr.write(FieldPacket.showUnknownEnchantFailResult((byte) 0)); break; } } @Handler(op = InHeader.USER_SKILL_LEARN_ITEM_USE_REQUEST) public static void handleUserLearnItemUseRequest(Client c, InPacket inPacket) { c.verifyTick(inPacket); short pos = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); Char chr = c.getChr(); ItemInfo ii = ItemData.getItemInfoByID(itemID); Item item = chr.getConsumeInventory().getItemBySlot(pos); if (ii == null || !chr.hasItem(itemID) || item == null || item.getItemId() != itemID || !chr.isInValidState()) { chr.chatMessage("Could not find that item."); return; } int masterLevel = ii.getMasterLv(); int reqSkillLv = ii.getReqSkillLv(); int skillid = 0; Map vals = ii.getScrollStats(); int chance = vals.getOrDefault(ScrollStat.success, 100); for (int skill : ii.getSkills()) { if (chr.hasSkill(skill)) { skillid = skill; break; } } Skill skill = chr.getSkill(skillid); if (skill == null) { chr.chatMessage(Notice2, "An error has occured. Mastery Book ID: " + itemID + ", skill ID: " + skillid + "."); chr.dispose(); return; } if (skillid == 0 || (skill.getMasterLevel() >= masterLevel) || skill.getCurrentLevel() < reqSkillLv) { chr.chatMessage(SystemNotice, "You cannot use this Mastery Book."); chr.dispose(); return; } if (skill.getCurrentLevel() > reqSkillLv && skill.getMasterLevel() < masterLevel) { chr.chatMessage(Mob, "Success Chance: " + chance + "%."); chr.consumeItem(itemID, 1); if (Util.succeedProp(chance)) { skill.setMasterLevel(masterLevel); chr.addSkill(skill); chr.write(WvsContext.changeSkillRecordResult(skill)); chr.chatMessage(Notice2, "[Mastery Book] Item id: " + itemID + " set Skill id: " + skillid + "'s Master Level to: " + masterLevel + "."); } else { chr.chatMessage(Notice2, "[Mastery Book] Item id: " + itemID + " was used, however it was unsuccessful."); } } chr.dispose(); } @Handler(op = InHeader.SOCKET_CREATE_REQUEST) public static void handleSocketCreateRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); c.verifyTick(inPacket); short uPos = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); short ePos = inPacket.decodeShort(); Item item = chr.getConsumeInventory().getItemBySlot(uPos); Equip equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (equip == null || item == null || item.getItemId() != itemID) { log.error("Unknown equip or mismatching use items."); return; } int success = 0; if (equip.getSocket(0) == ItemConstants.INACTIVE_SOCKET && ItemConstants.canEquipHavePotential(equip)) { chr.consumeItem(item); equip.setSocket(0, ItemConstants.EMPTY_SOCKET_ID); } else { success = 1; } c.write(FieldPacket.socketCreateResult(success)); equip.updateToChar(chr); } @Handler(op = InHeader.SOCKET_CREATE_RESULT) public static void handleSocketCreateResult(Client c, InPacket inPacket) { c.write(FieldPacket.socketCreateResult(inPacket.decodeInt() + 2)); } @Handler(op = InHeader.NEBULITE_INSERT_REQUEST) public static void handleNebuliteInsertRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); c.verifyTick(inPacket); short nebPos = inPacket.decodeShort(); int nebID = inPacket.decodeInt(); Item item = chr.getInstallInventory().getItemBySlot(nebPos); short ePos = inPacket.decodeShort(); Equip equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (item == null || equip == null || item.getItemId() != nebID || !ItemConstants.isNebulite(item.getItemId())) { log.error("Nebulite or equip was not found when inserting."); chr.dispose(); return; } if (equip.getSocket(0) != ItemConstants.EMPTY_SOCKET_ID) { log.error("Tried to Nebulite an item without an empty socket."); chr.chatMessage("You can only insert a Nebulite into empty socket slots."); chr.dispose(); return; } if (!ItemConstants.nebuliteFitsEquip(equip, item)) { chr.getOffenseManager().addOffense(String.format("Character %d attempted to use a nebulite (%d) that doesn't fit an equip (%d).", chr.getId(), item.getItemId(), equip.getItemId())); chr.chatMessage("The nebulite cannot be mounted on this equip."); chr.dispose(); return; } chr.consumeItem(item); equip.setSocket(0, nebID % ItemConstants.NEBULITE_BASE_ID); equip.updateToChar(chr); } @Handler(op = InHeader.USER_ITEM_SKILL_SOCKET_UPGRADE_ITEM_USE_REQUEST) public static void handleUserItemSkillSocketUpdateItemUseRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); c.verifyTick(inPacket); short uPos = inPacket.decodeShort(); short ePos = inPacket.decodeShort(); Item item = chr.getConsumeInventory().getItemBySlot(uPos); Equip equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (item == null || equip == null || !ItemConstants.isWeapon(equip.getItemId()) || !ItemConstants.isSoulEnchanter(item.getItemId()) || equip.getrLevel() + equip.getiIncReq() < ItemConstants.MIN_LEVEL_FOR_SOUL_SOCKET) { chr.dispose(); return; } int successProp = ItemData.getItemInfoByID(item.getItemId()).getScrollStats().get(ScrollStat.success); boolean success = Util.succeedProp(successProp); if (success) { equip.setSoulSocketId((short) (item.getItemId() % ItemConstants.SOUL_ENCHANTER_BASE_ID)); equip.updateToChar(chr); } chr.getField().broadcastPacket(UserPacket.showItemSkillSocketUpgradeEffect(chr.getId(), success)); chr.consumeItem(item); } @Handler(op = InHeader.USER_ITEM_SKILL_OPTION_UPGRADE_ITEM_USE_REQUEST) public static void handleUserItemSkillOptionUpdateItemUseRequest(Client c, InPacket inPacket) { Char chr = c.getChr(); c.verifyTick(inPacket); short uPos = inPacket.decodeShort(); short ePos = inPacket.decodeShort(); Item item = chr.getConsumeInventory().getItemBySlot(uPos); Equip equip = (Equip) chr.getEquipInventory().getItemBySlot(ePos); if (item == null || equip == null || !ItemConstants.isWeapon(equip.getItemId()) || !ItemConstants.isSoul(item.getItemId()) || equip.getSoulSocketId() == 0) { chr.dispose(); return; } equip.setSoulOptionId((short) (1 + item.getItemId() % ItemConstants.SOUL_ITEM_BASE_ID)); short option = ItemConstants.getSoulOptionFromSoul(item.getItemId()); //Currently does nothing if (option == 0) { option = (short) ItemConstants.getRandomSoulOption(); } equip.setSoulOption(option); equip.updateToChar(chr); chr.consumeItem(item); chr.getField().broadcastPacket(UserPacket.showItemSkillOptionUpgradeEffect(chr.getId(), true, false, ePos, uPos)); } @Handler(op = InHeader.USER_WEAPON_TEMP_ITEM_OPTION_REQUEST) public static void handleUserWeaponTempItemOptionRequest(Char chr, InPacket inPacket) { TemporaryStatManager tsm = chr.getTemporaryStatManager(); if (tsm.hasStat(CharacterTemporaryStat.SoulMP) && tsm.getOption(CharacterTemporaryStat.SoulMP).nOption >= ItemConstants.MAX_SOUL_CAPACITY) { Option o = new Option(); o.nOption = tsm.getOption(CharacterTemporaryStat.SoulMP).nOption; o.xOption = tsm.getOption(CharacterTemporaryStat.SoulMP).xOption; o.rOption = ItemConstants.getSoulSkillFromSoulID( ((Equip) chr.getEquippedItemByBodyPart(BodyPart.Weapon)).getSoulOptionId() ); tsm.putCharacterStatValue(CharacterTemporaryStat.FullSoulMP, o); tsm.sendSetStatPacket(); } chr.dispose(); } @Handler(op = InHeader.USER_PROTECT_BUFF_DIE_ITEM_REQUEST) public static void handleUserProtectBuffDieItemRequest(Char chr, InPacket inPacket) { chr.getClient().verifyTick(inPacket); boolean used = inPacket.decodeByte() != 0; if (used) { // grabs the first one from the list of buffItems Item buffProtector = chr.getBuffProtectorItem(); if (buffProtector != null) { chr.setBuffProtector(true); chr.consumeItem(buffProtector); chr.write(UserLocal.setBuffProtector(buffProtector.getItemId(), true)); } else { chr.getOffenseManager().addOffense(String.format("Character id %d tried to use a buff without having the appropriate item.", chr.getId())); } } } @Handler(op = InHeader.USER_DEFAULT_WING_ITEM) public static void handleUserDefaultWingItem(Char chr, InPacket inPacket) { int wingItem = inPacket.decodeInt(); if (wingItem == 5010093) { // AB chr.getAvatarData().getCharacterStat().setWingItem(wingItem); chr.getField().broadcastPacket(UserRemote.setDefaultWingItem(chr)); } } @Handler(op = InHeader.USER_RECIPE_OPEN_ITEM_USE_REQUEST) public static void handleUserRecipeOpenItemUseRequest(Char chr, InPacket inPacket) { chr.getClient().verifyTick(inPacket); short pos = inPacket.decodeShort();// // nPOS int itemID = inPacket.decodeInt();// nItemID Item item = chr.getInventoryByType(CONSUME).getItemBySlot(pos); if (item == null || item.getItemId() != itemID || !chr.isInValidState()) { chr.dispose(); return; } if (chr != null && chr.getHP() > 0 && ItemConstants.isRecipeOpenItem(itemID)) { ItemInfo recipe = ItemData.getItemInfoByID(itemID); if (recipe != null) { int recipeID = recipe.getSpecStats().getOrDefault(SpecStat.recipe, 0); int reqSkillLevel = recipe.getSpecStats().getOrDefault(SpecStat.reqSkillLevel, 0); MakingSkillRecipe msr = SkillData.getRecipeById(recipeID); if (msr != null && msr.isNeedOpenItem()) { if (chr.getSkillLevel(msr.getReqSkillID()) < reqSkillLevel || chr.getSkillLevel(recipeID) > 0) { return; } chr.addSkill(recipeID, 1, 1); } } } } @Handler(op = InHeader.USER_ACTIVATE_NICK_ITEM) public static void handleUserActivateNickItem(Client c, InPacket inPacket) { Char chr = c.getChr(); int nickItem = inPacket.decodeInt(); if (nickItem == 0 || nickItem / 10000 == 370 && chr.hasItem(nickItem)) { chr.setActiveNickItemID(nickItem); chr.getField().broadcastPacket(UserRemote.setActiveNickItem(chr, null), chr); } } @Handler(op = InHeader.USER_CHAR_SLOT_INC_ITEM_USE_REQUEST) public static void addChrSlot(Client c, InPacket inPacket) { Account user = c.getAccount(); int currentSlots = user.getCharacterSlots(); user.setCharacterSlots(currentSlots + 1); } @Handler(op = InHeader.USER_LOTTERY_ITEM_USE_REQUEST) public static void handleUserLotteryItemUseRequest(Char chr, InPacket inPacket) { short pos = inPacket.decodeShort(); int itemID = inPacket.decodeInt(); inPacket.decodeByte(); // bSendForUI inPacket.decodeByte(); // bLogStart inPacket.decodeInt(); // 1 Item item; ItemInfo itemInfo; if (itemID / 1000 != 2028 || (item = chr.getConsumeInventory().getItemBySlot(pos)) == null || itemID != item.getItemId() || ((itemInfo = ItemData.getItemInfoByID(itemID)) == null)) { chr.dispose("Not found."); return; } Object reward = itemInfo.getRandomReward(); if (reward instanceof Item) { chr.addItemToInventory((Item) reward); } else if (reward instanceof Integer && (int) reward != 0) { chr.addMoney((int) reward); } else { chr.chatMessage("Oh, you're unlucky."); // should not happen } chr.consumeItem(item); chr.dispose(); } }