ItemHandler.java:
ItemHandler.java
----------------------------------------------------------------------------------------------------
import net.sf.l2j.gameserver.handler.itemhandlers.Keys;
+import net.sf.l2j.gameserver.handler.itemhandlers.LifestoneHandler;
registerHandler(new SoulCrystals());
registerHandler(new SpiritShots());
registerHandler(new SummonItems());
+ registerHandler(new LifestoneHandler());
====================================================================================================
LifestoneHandler.java:
LifestoneHandler.java
----------------------------------------------------------------------------------------------------
package net.sf.l2j.gameserver.handler.itemhandlers;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.sf.l2j.gameserver.data.LifeStoneManager;
-import net.sf.l2j.gameserver.data.xml.AugmentationData;
+import net.sf.l2j.gameserver.data.xml.ItemData;
+import net.sf.l2j.gameserver.enums.items.CrystalType;
import net.sf.l2j.gameserver.handler.IItemHandler;
-import net.sf.l2j.gameserver.model.Augmentation;
import net.sf.l2j.gameserver.model.actor.Playable;
import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService.OperationResult;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService.ValidationStatus;
import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
-import net.sf.l2j.gameserver.model.item.kind.Weapon;
-import net.sf.l2j.gameserver.network.serverpackets.ItemList;
+import net.sf.l2j.gameserver.model.item.kind.Item;
import net.sf.l2j.gameserver.network.serverpackets.MagicSkillUse;
import net.sf.l2j.gameserver.network.serverpackets.PlaySound;
-public class LifestoneHandler implements IItemHandler
+/**
+ * Applies a Life Stone directly to the weapon equipped by the player.
+ * <p>
+ * By Atrein:
+ * Delegates validation, charging, unequip/re-equip, augmentation persistence and
+ * rollback to {@link AugmentationService}. The handler only resolves the equipped
+ * target, presents localized results and plays the success feedback.
+ */
+public final class LifestoneHandler implements IItemHandler
{
- private static final Logger LOG = Logger.getLogger(LifestoneHandler.class.getName());
-
- private static final int GEMSTONE_D = 2130;
- private static final int GEMSTONE_C = 2131;
-
private static final int EFFECT_SKILL_ID = 2025;
private static final int EFFECT_SKILL_LEVEL = 1;
private static final int EFFECT_HIT_TIME = 1000;
private static final int EFFECT_REUSE_DELAY = 0;
private static final String EFFECT_SOUND = "ItemSound3.armor_angel";
-
- private static final String MSG_EQUIP_WEAPON = "You must equip a weapon to insert a Life Stone.";
- private static final String MSG_WEAPON_ONLY = "Life Stones can only be used on weapons.";
- private static final String MSG_ALREADY_AUGMENTED = "This weapon is already augmented.";
- private static final String MSG_ONLY_C_TO_S = "Only C-S grade weapons can be augmented.";
- private static final String MSG_NOT_ENOUGH_GEMSTONES = "You do not have enough Gemstones for this augmentation.";
- private static final String MSG_GENERATION_FAILED = "Augmentation failed. Please try again.";
- private static final String MSG_LIFESTONE_CONSUME_FAILED = "Failed to consume the Life Stone.";
- private static final String MSG_GEMSTONE_CONSUME_FAILED = "Failed to consume Gemstones. Life Stone was returned.";
- private static final String MSG_STATE_CHANGED = "Weapon state changed. Consumed items were returned.";
- private static final String MSG_APPLY_FAILED = "Augmentation apply failed. Consumed items were returned.";
- private static final String MSG_SUCCESS = "The augmentation was successful.";
-
- public int[] getItemIds()
+
+ @Override
+ public void useItem(Playable playable, ItemInstance lifeStoneItem, boolean forceUse)
{
- return LifeStoneManager.getAllIdsArray();
- }
-
- @Override
- public void useItem(Playable playable, ItemInstance lifeStone, boolean ctrl)
- {
- if (!(playable instanceof Player) || lifeStone == null)
+ if (!(playable instanceof Player player) || lifeStoneItem == null)
return;
-
- final Player player = (Player) playable;
- final ItemInstance weapon = player.getActiveWeaponInstance();
- if (weapon == null)
+
+ final ItemInstance targetItem = player.getActiveWeaponInstance();
+ if (targetItem == null)
{
- fail(player, MSG_EQUIP_WEAPON, false);
+ player.sendMessage(player.getSysString(10_279));
return;
}
-
- if (!(weapon.getItem() instanceof Weapon))
+
+ final CrystalType crystalType = targetItem.getItem().getCrystalType();
+ final int gemstoneId = crystalType.getGemstoneId();
+ final int gemstoneCount = crystalType.getGemstoneCount();
+ final ItemInstance gemstoneItem = gemstoneId > 0 ? player.getInventory().getItemByItemId(gemstoneId) : null;
+
+ final OperationResult result = AugmentationService.getInstance().augment(player, targetItem, lifeStoneItem, gemstoneItem, gemstoneCount, AugmentationService.AugmentationMode.EQUIPPED_WEAPON);
+ if (!result.isSuccess())
{
- fail(player, MSG_WEAPON_ONLY, false);
+ handleFailure(player, result, gemstoneId, gemstoneCount);
return;
}
-
- if (weapon.isAugmented())
+
+ playSuccessEffect(player);
+ player.sendMessage(player.getSysString(result.status() == ValidationStatus.SUCCESS_REEQUIP_FAILED ? 10_289 : 10_288));
+ }
+
+ private static void handleFailure(Player player, OperationResult result, int gemstoneId, int gemstoneCount)
+ {
+ if (AugmentationService.getInstance().sendStandardFailure(player, result.status()))
+ return;
+
+ switch (result.status())
{
- fail(player, MSG_ALREADY_AUGMENTED, false);
- return;
+ case TARGET_MISSING -> player.sendMessage(player.getSysString(10_279));
+ case LIFE_STONE_MISSING, LIFE_STONE_NOT_OWNED, LIFE_STONE_INVALID_LOCATION, LIFE_STONE_UNKNOWN -> player.sendMessage(player.getSysString(10_280));
+ case TARGET_LOW_GRADE -> player.sendMessage(player.getSysString(10_281));
+ case TARGET_ALREADY_AUGMENTED -> player.sendMessage(player.getSysString(10_283));
+ case GEMSTONE_MISSING, GEMSTONE_NOT_OWNED, GEMSTONE_INVALID_LOCATION, GEMSTONE_WRONG_ITEM, GEMSTONE_COUNT_MISMATCH, GEMSTONE_INSUFFICIENT -> player.sendMessage(player.getSysString(10_284, gemstoneCount, getItemName(gemstoneId)));
+ case AUGMENTATION_GENERATION_FAILED -> player.sendMessage(player.getSysString(10_285));
+ case TARGET_STATE_CHANGED, TARGET_UNEQUIP_FAILED -> player.sendMessage(player.getSysString(10_290));
+ case ROLLBACK_FAILED -> player.sendMessage(player.getSysString(10_287));
+ case LIFE_STONE_CONSUME_FAILED, GEMSTONE_CONSUME_FAILED, AUGMENTATION_APPLY_FAILED -> player.sendMessage(player.getSysString(10_286));
+ default -> player.sendMessage(player.getSysString(10_282));
}
-
- final GemstoneRequirement requirement = GemstoneRequirement.forWeapon(weapon);
- if (requirement == null)
- {
- fail(player, MSG_ONLY_C_TO_S, false);
- return;
- }
-
- if (!hasRequiredGemstones(player, requirement))
- {
- fail(player, MSG_NOT_ENOUGH_GEMSTONES, false);
- return;
- }
-
- final int lifeStoneId = lifeStone.getItemId();
- final int augmentationGrade = LifeStoneManager.getGrade(lifeStoneId);
- final int augmentationLevel = LifeStoneManager.getLevel(lifeStoneId);
- final Augmentation augmentation = AugmentationData.getInstance().generateRandomAugmentation(augmentationLevel, augmentationGrade);
- if (augmentation == null)
- {
- LOG.warning("[LifestoneHandler] Failed to generate augmentation for player=" + player.getName() + ", lifeStoneId=" + lifeStoneId + ", grade=" + augmentationGrade + ", level=" + augmentationLevel);
- fail(player, MSG_GENERATION_FAILED, false);
- return;
- }
-
- if (!consumeLifeStone(player, lifeStone))
- {
- fail(player, MSG_LIFESTONE_CONSUME_FAILED, true);
- return;
- }
-
- if (!consumeGemstones(player, requirement))
- {
- rollbackLifeStone(player, lifeStoneId);
- fail(player, MSG_GEMSTONE_CONSUME_FAILED, true);
- return;
- }
-
- if (!isWeaponStateValid(player, weapon))
- {
- rollbackMaterials(player, lifeStoneId, requirement);
- fail(player, MSG_STATE_CHANGED, true);
- return;
- }
-
- try
- {
- weapon.setAugmentation(augmentation, player);
- }
- catch (RuntimeException e)
- {
- rollbackMaterials(player, lifeStoneId, requirement);
- LOG.log(Level.WARNING, "[LifestoneHandler] Failed to apply augmentation for player=" + player.getName() + ", weaponObjectId=" + weapon.getObjectId(), e);
- fail(player, MSG_APPLY_FAILED, true);
- return;
- }
-
- playSuccessEffect(player);
- refreshInventory(player);
- player.sendMessage(MSG_SUCCESS);
}
-
- private static boolean hasRequiredGemstones(Player player, GemstoneRequirement requirement)
+
+ private static String getItemName(int itemId)
{
- final ItemInstance gemstones = player.getInventory().getItemByItemId(requirement.itemId);
- return gemstones != null && gemstones.getCount() >= requirement.count;
+ final Item item = ItemData.getInstance().getTemplate(itemId);
+ return item == null ? "item ID " + itemId : item.getName();
}
-
- private static boolean consumeLifeStone(Player player, ItemInstance lifeStone)
- {
- return player.destroyItem(lifeStone, 1, false);
- }
-
- private static boolean consumeGemstones(Player player, GemstoneRequirement requirement)
- {
- return player.destroyItemByItemId(requirement.itemId, requirement.count, false);
- }
-
- private static boolean isWeaponStateValid(Player player, ItemInstance weapon)
- {
- return player.getActiveWeaponInstance() == weapon && !weapon.isAugmented();
- }
-
- private static void rollbackLifeStone(Player player, int lifeStoneId)
- {
- player.addItem(lifeStoneId, 1, false);
- }
-
- private static void rollbackMaterials(Player player, int lifeStoneId, GemstoneRequirement requirement)
- {
- rollbackLifeStone(player, lifeStoneId);
- player.addItem(requirement.itemId, requirement.count, false);
- }
-
- private static void fail(Player player, String message, boolean refreshInventory)
- {
- player.sendMessage(message);
- if (refreshInventory)
- refreshInventory(player);
- }
-
+
private static void playSuccessEffect(Player player)
{
final MagicSkillUse effect = new MagicSkillUse(player, player, EFFECT_SKILL_ID, EFFECT_SKILL_LEVEL, EFFECT_HIT_TIME, EFFECT_REUSE_DELAY);
player.broadcastPacket(effect);
player.sendPacket(new PlaySound(EFFECT_SOUND));
}
-
- private static void refreshInventory(Player player)
- {
- player.sendPacket(new ItemList(player, false));
- }
-
- private static final class GemstoneRequirement
- {
- private final int itemId;
- private final int count;
-
- private GemstoneRequirement(int itemId, int count)
- {
- this.itemId = itemId;
- this.count = count;
- }
-
- private static GemstoneRequirement forWeapon(ItemInstance weapon)
- {
- switch (weapon.getItem().getCrystalType())
- {
- case C:
- return new GemstoneRequirement(GEMSTONE_D, 30);
- case B:
- return new GemstoneRequirement(GEMSTONE_D, 20);
- case A:
- return new GemstoneRequirement(GEMSTONE_C, 25);
- case S:
- return new GemstoneRequirement(GEMSTONE_C, 35);
- default:
- return null;
- }
- }
- }
}
AugmentationService.java:
AugmentationService.java — новый файл
----------------------------------------------------------------------------------------------------
НОВЫЙ ФАЙЛ — весь код добавляется:
+package net.sf.l2j.gameserver.model.enchant;
+
+import net.sf.l2j.commons.logging.CLogger;
+
+import net.sf.l2j.gameserver.data.LifeStoneManager;
+import net.sf.l2j.gameserver.data.xml.AugmentationData;
+import net.sf.l2j.gameserver.enums.ShortcutType;
+import net.sf.l2j.gameserver.enums.actors.MissionType;
+import net.sf.l2j.gameserver.enums.items.CrystalType;
+import net.sf.l2j.gameserver.enums.items.ItemLocation;
+import net.sf.l2j.gameserver.enums.items.WeaponType;
+import net.sf.l2j.gameserver.model.Augmentation;
+import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.item.LifeStone;
+import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
+import net.sf.l2j.gameserver.model.item.kind.Weapon;
+import net.sf.l2j.gameserver.network.SystemMessageId;
+import net.sf.l2j.gameserver.network.serverpackets.ItemList;
+
+/**
+ * Coordinates augmentation validation, payment, application and rollback.
+ * <p>
+ * By Atrein:
+ * Provides one server-side augmentation workflow for standard refinement packets
+ * and direct-use Life Stones. The operation validates every mutable resource again
+ * under the player's inventory lock, applies the augmentation while unequipped and
+ * restores consumed materials when application fails.
+ */
+public final class AugmentationService
+{
+ private static final CLogger LOGGER = new CLogger(AugmentationService.class.getName());
+
+ private AugmentationService()
+ {
+ }
+
+ /**
+ * @param player The player to validate.
+ * @return The validation status.
+ */
+ public ValidationStatus validatePlayer(Player player)
+ {
+ if (player == null)
+ return ValidationStatus.INVALID_PLAYER;
+ if (player.isOperating())
+ return ValidationStatus.PLAYER_OPERATING;
+ if (player.isProcessingTransaction())
+ return ValidationStatus.PLAYER_TRADING;
+ if (player.isDead())
+ return ValidationStatus.PLAYER_DEAD;
+ if (player.isParalyzed())
+ return ValidationStatus.PLAYER_PARALYZED;
+ if (player.isFishing())
+ return ValidationStatus.PLAYER_FISHING;
+ if (player.isSitting())
+ return ValidationStatus.PLAYER_SITTING;
+ if (player.isCursedWeaponEquipped())
+ return ValidationStatus.CURSED_WEAPON_EQUIPPED;
+ return ValidationStatus.SUCCESS;
+ }
+
+ /**
+ * @param player The player requesting augmentation.
+ * @param targetItem The target item.
+ * @return The validation status.
+ */
+ public ValidationStatus validateTarget(Player player, ItemInstance targetItem)
+ {
+ final ValidationStatus playerStatus = validatePlayer(player);
+ if (playerStatus != ValidationStatus.SUCCESS)
+ return playerStatus;
+ if (targetItem == null)
+ return ValidationStatus.TARGET_MISSING;
+ if (targetItem.getOwnerId() != player.getObjectId())
+ return ValidationStatus.TARGET_NOT_OWNED;
+ if (targetItem.isAugmented())
+ return ValidationStatus.TARGET_ALREADY_AUGMENTED;
+ if (targetItem.isHeroItem())
+ return ValidationStatus.TARGET_HERO_ITEM;
+ if (targetItem.isShadowItem())
+ return ValidationStatus.TARGET_SHADOW_ITEM;
+ if (targetItem.getItem().getCrystalType().isLesser(CrystalType.C))
+ return ValidationStatus.TARGET_LOW_GRADE;
+ if (targetItem.getLocation() != ItemLocation.INVENTORY && targetItem.getLocation() != ItemLocation.PAPERDOLL)
+ return ValidationStatus.TARGET_INVALID_LOCATION;
+ if (!(targetItem.getItem() instanceof Weapon weapon))
+ return ValidationStatus.TARGET_NOT_WEAPON;
+ if (weapon.getItemType() == WeaponType.NONE)
+ return ValidationStatus.TARGET_FISTS;
+ if (weapon.getItemType() == WeaponType.FISHINGROD)
+ return ValidationStatus.TARGET_FISHING_ROD;
+ return ValidationStatus.SUCCESS;
+ }
+
+ /**
+ * @param player The player requesting augmentation.
+ * @param targetItem The target item.
+ * @param lifeStoneItem The Life Stone item.
+ * @return The validation status.
+ */
+ public ValidationStatus validateLifeStone(Player player, ItemInstance targetItem, ItemInstance lifeStoneItem)
+ {
+ final ValidationStatus targetStatus = validateTarget(player, targetItem);
+ if (targetStatus != ValidationStatus.SUCCESS)
+ return targetStatus;
+ if (lifeStoneItem == null)
+ return ValidationStatus.LIFE_STONE_MISSING;
+ if (lifeStoneItem.getOwnerId() != player.getObjectId())
+ return ValidationStatus.LIFE_STONE_NOT_OWNED;
+ if (lifeStoneItem.getLocation() != ItemLocation.INVENTORY)
+ return ValidationStatus.LIFE_STONE_INVALID_LOCATION;
+
+ final LifeStone lifeStone = LifeStoneManager.getLifeStone(lifeStoneItem.getItemId());
+ if (lifeStone == null)
+ return ValidationStatus.LIFE_STONE_UNKNOWN;
+ if (player.getStatus().getLevel() < lifeStone.getPlayerLevel())
+ return ValidationStatus.PLAYER_LEVEL_TOO_LOW;
+ return ValidationStatus.SUCCESS;
+ }
+
+ /**
+ * @param player The player requesting augmentation.
+ * @param targetItem The target item.
+ * @param lifeStoneItem The Life Stone item.
+ * @param gemstoneItem The gemstone stack.
+ * @param requestedGemstoneCount The amount submitted by the caller.
+ * @return The validation status.
+ */
+ public ValidationStatus validate(Player player, ItemInstance targetItem, ItemInstance lifeStoneItem, ItemInstance gemstoneItem, int requestedGemstoneCount)
+ {
+ final ValidationStatus lifeStoneStatus = validateLifeStone(player, targetItem, lifeStoneItem);
+ if (lifeStoneStatus != ValidationStatus.SUCCESS)
+ return lifeStoneStatus;
+ if (gemstoneItem == null)
+ return ValidationStatus.GEMSTONE_MISSING;
+ if (gemstoneItem.getOwnerId() != player.getObjectId())
+ return ValidationStatus.GEMSTONE_NOT_OWNED;
+ if (gemstoneItem.getLocation() != ItemLocation.INVENTORY)
+ return ValidationStatus.GEMSTONE_INVALID_LOCATION;
+
+ final CrystalType crystalType = targetItem.getItem().getCrystalType();
+ if (gemstoneItem.getItemId() != crystalType.getGemstoneId())
+ return ValidationStatus.GEMSTONE_WRONG_ITEM;
+ if (requestedGemstoneCount != crystalType.getGemstoneCount())
+ return ValidationStatus.GEMSTONE_COUNT_MISMATCH;
+ if (gemstoneItem.getCount() < requestedGemstoneCount)
+ return ValidationStatus.GEMSTONE_INSUFFICIENT;
+ return ValidationStatus.SUCCESS;
+ }
+
+ /**
+ * Performs one complete augmentation operation.
+ * @param player The player requesting augmentation.
+ * @param targetItem The weapon to augment.
+ * @param lifeStoneItem The Life Stone to consume.
+ * @param gemstoneItem The gemstone stack to consume.
+ * @param requestedGemstoneCount The exact server-expected gemstone amount.
+ * @param mode The entry-point policy controlling target selection and re-equipping.
+ * @return The immutable operation result.
+ */
+ public OperationResult augment(Player player, ItemInstance targetItem, ItemInstance lifeStoneItem, ItemInstance gemstoneItem, int requestedGemstoneCount, AugmentationMode mode)
+ {
+ ValidationStatus status = validate(player, targetItem, lifeStoneItem, gemstoneItem, requestedGemstoneCount);
+ if (status != ValidationStatus.SUCCESS)
+ return OperationResult.failure(status);
+ if (mode == null)
+ return OperationResult.failure(ValidationStatus.TARGET_STATE_CHANGED);
+ if (mode == AugmentationMode.EQUIPPED_WEAPON && (player.getActiveWeaponInstance() != targetItem || !targetItem.isEquipped()))
+ return OperationResult.failure(ValidationStatus.TARGET_STATE_CHANGED);
+
+ final LifeStone lifeStone = LifeStoneManager.getLifeStone(lifeStoneItem.getItemId());
+ final Augmentation augmentation;
+ try
+ {
+ augmentation = AugmentationData.getInstance().generateRandomAugmentation(lifeStone.getLevel(), lifeStone.getGrade());
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.error("Failed to generate augmentation for player [{}], Life Stone [{}].", e, player.getName(), lifeStoneItem.getItemId());
+ return OperationResult.failure(ValidationStatus.AUGMENTATION_GENERATION_FAILED);
+ }
+ if (augmentation == null)
+ return OperationResult.failure(ValidationStatus.AUGMENTATION_GENERATION_FAILED);
+
+ synchronized (player.getInventory())
+ {
+ status = validate(player, targetItem, lifeStoneItem, gemstoneItem, requestedGemstoneCount);
+ if (status != ValidationStatus.SUCCESS)
+ return OperationResult.failure(status);
+ if (mode == AugmentationMode.EQUIPPED_WEAPON && (player.getActiveWeaponInstance() != targetItem || !targetItem.isEquipped()))
+ return OperationResult.failure(ValidationStatus.TARGET_STATE_CHANGED);
+
+ final boolean wasEquipped = targetItem.isEquipped();
+ final int previousSlot = targetItem.getLocationSlot();
+ if (wasEquipped && !unequip(player, targetItem, previousSlot))
+ return OperationResult.failure(ValidationStatus.TARGET_UNEQUIP_FAILED);
+
+ boolean lifeStoneConsumed = false;
+ boolean gemstonesConsumed = false;
+ boolean augmentationApplied = false;
+ try
+ {
+ lifeStoneConsumed = player.destroyItem(lifeStoneItem, 1, false);
+ if (!lifeStoneConsumed)
+ {
+ reEquipAfterFailure(player, targetItem, wasEquipped);
+ return OperationResult.failure(ValidationStatus.LIFE_STONE_CONSUME_FAILED);
+ }
+
+ gemstonesConsumed = player.destroyItem(gemstoneItem, requestedGemstoneCount, false);
+ if (!gemstonesConsumed)
+ {
+ final boolean refunded = refundMaterials(player, lifeStoneItem.getItemId(), 1, 0, 0);
+ reEquipAfterFailure(player, targetItem, wasEquipped);
+ return OperationResult.failure(refunded ? ValidationStatus.GEMSTONE_CONSUME_FAILED : ValidationStatus.ROLLBACK_FAILED);
+ }
+
+ augmentationApplied = targetItem.setAugmentation(augmentation, player);
+ if (!augmentationApplied)
+ {
+ final boolean refunded = refundMaterials(player, lifeStoneItem.getItemId(), 1, gemstoneItem.getItemId(), requestedGemstoneCount);
+ reEquipAfterFailure(player, targetItem, wasEquipped);
+ return OperationResult.failure(refunded ? ValidationStatus.AUGMENTATION_APPLY_FAILED : ValidationStatus.ROLLBACK_FAILED);
+ }
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.error("Augmentation operation failed for player [{}], weapon object ID [{}].", e, player.getName(), targetItem.getObjectId());
+ boolean augmentationRolledBack = true;
+ if (augmentationApplied || targetItem.getAugmentation() == augmentation)
+ {
+ try
+ {
+ targetItem.removeAugmentation(player);
+ augmentationRolledBack = !targetItem.isAugmented();
+ }
+ catch (RuntimeException rollbackException)
+ {
+ augmentationRolledBack = false;
+ LOGGER.error("Failed to remove partially applied augmentation from weapon object ID [{}].", rollbackException, targetItem.getObjectId());
+ }
+ }
+ final boolean refunded = refundMaterials(player, lifeStoneItem.getItemId(), lifeStoneConsumed ? 1 : 0, gemstoneItem.getItemId(), gemstonesConsumed ? requestedGemstoneCount : 0);
+ reEquipAfterFailure(player, targetItem, wasEquipped);
+ return OperationResult.failure(augmentationRolledBack && refunded ? ValidationStatus.AUGMENTATION_APPLY_FAILED : ValidationStatus.ROLLBACK_FAILED);
+ }
+
+ boolean reEquipped = true;
+ if (wasEquipped && mode == AugmentationMode.EQUIPPED_WEAPON)
+ reEquipped = reEquip(player, targetItem);
+
+ refreshAfterSuccess(player, targetItem);
+ return new OperationResult(reEquipped ? ValidationStatus.SUCCESS : ValidationStatus.SUCCESS_REEQUIP_FAILED, augmentation, reEquipped);
+ }
+ }
+
+ /**
+ * Sends the standard client message associated with a validation status.
+ * @param player The player receiving the message.
+ * @param status The validation status.
+ * @return {@code true} when a specific standard message was sent.
+ */
+ public boolean sendStandardFailure(Player player, ValidationStatus status)
+ {
+ if (player == null)
+ return false;
+
+ final SystemMessageId message = switch (status)
+ {
+ case PLAYER_OPERATING -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_A_PRIVATE_STORE_OR_PRIVATE_WORKSHOP_IS_IN_OPERATION;
+ case PLAYER_TRADING -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_TRADING;
+ case PLAYER_DEAD -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_DEAD;
+ case PLAYER_PARALYZED -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_PARALYZED;
+ case PLAYER_FISHING -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_FISHING;
+ case PLAYER_SITTING -> SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_SITTING_DOWN;
+ case PLAYER_LEVEL_TOO_LOW -> SystemMessageId.HARDENER_LEVEL_TOO_HIGH;
+ default -> null;
+ };
+ if (message == null)
+ return false;
+ player.sendPacket(message);
+ return true;
+ }
+
+ private static boolean unequip(Player player, ItemInstance targetItem, int slot)
+ {
+ player.getInventory().unequipItemInSlotAndRecord(slot);
+ player.refreshExpertisePenalty();
+ player.broadcastUserInfo();
+ return !targetItem.isEquipped() && targetItem.getLocation() == ItemLocation.INVENTORY;
+ }
+
+ private static boolean reEquip(Player player, ItemInstance targetItem)
+ {
+ try
+ {
+ player.getInventory().equipItemAndRecord(targetItem);
+ player.refreshExpertisePenalty();
+ player.broadcastUserInfo();
+ return targetItem.isEquipped();
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.error("Failed to re-equip augmented weapon object ID [{}] for player [{}].", e, targetItem.getObjectId(), player.getName());
+ return false;
+ }
+ }
+
+ private static void reEquipAfterFailure(Player player, ItemInstance targetItem, boolean wasEquipped)
+ {
+ if (wasEquipped && !targetItem.isEquipped())
+ reEquip(player, targetItem);
+ }
+
+ private static boolean refundMaterials(Player player, int lifeStoneId, int lifeStoneCount, int gemstoneId, int gemstoneCount)
+ {
+ boolean success = true;
+ if (lifeStoneCount > 0)
+ {
+ try
+ {
+ if (player.addItem(lifeStoneId, lifeStoneCount, false) == null)
+ success = false;
+ }
+ catch (RuntimeException e)
+ {
+ success = false;
+ LOGGER.error("Failed to refund Life Stone [{}] x[{}] to player [{}].", e, lifeStoneId, lifeStoneCount, player.getName());
+ }
+ if (!success)
+ LOGGER.error("Life Stone refund was not completed for item [{}] x[{}], player [{}].", lifeStoneId, lifeStoneCount, player.getName());
+ }
+ if (gemstoneCount > 0)
+ {
+ boolean gemstoneRefunded = true;
+ try
+ {
+ gemstoneRefunded = player.addItem(gemstoneId, gemstoneCount, false) != null;
+ }
+ catch (RuntimeException e)
+ {
+ gemstoneRefunded = false;
+ LOGGER.error("Failed to refund gemstones [{}] x[{}] to player [{}].", e, gemstoneId, gemstoneCount, player.getName());
+ }
+ if (!gemstoneRefunded)
+ {
+ LOGGER.error("Gemstone refund was not completed for item [{}] x[{}], player [{}].", gemstoneId, gemstoneCount, player.getName());
+ success = false;
+ }
+ }
+ return success;
+ }
+
+ private static void refreshAfterSuccess(Player player, ItemInstance targetItem)
+ {
+ try
+ {
+ player.getShortcutList().refreshShortcuts(shortcut -> targetItem.getObjectId() == shortcut.getId() && shortcut.getType() == ShortcutType.ITEM);
+ player.getMissions().update(MissionType.AUGMENT);
+ player.sendPacket(new ItemList(player, false));
+ player.broadcastUserInfo();
+ }
+ catch (RuntimeException e)
+ {
+ LOGGER.error("Augmentation succeeded, but client-state refresh failed for player [{}], weapon object ID [{}].", e, player.getName(), targetItem.getObjectId());
+ }
+ }
+
+ public static AugmentationService getInstance()
+ {
+ return SingletonHolder.INSTANCE;
+ }
+
+ /**
+ * Result of one augmentation operation.
+ * @param status The final operation status.
+ * @param augmentation The generated augmentation on success, otherwise {@code null}.
+ * @param reEquipped Whether an originally equipped weapon was successfully re-equipped.
+ */
+ public record OperationResult(ValidationStatus status, Augmentation augmentation, boolean reEquipped)
+ {
+ public static OperationResult failure(ValidationStatus status)
+ {
+ return new OperationResult(status, null, true);
+ }
+
+ public boolean isSuccess()
+ {
+ return status == ValidationStatus.SUCCESS || status == ValidationStatus.SUCCESS_REEQUIP_FAILED;
+ }
+ }
+
+ public enum AugmentationMode
+ {
+ STANDARD,
+ EQUIPPED_WEAPON
+ }
+
+ public enum ValidationStatus
+ {
+ SUCCESS,
+ SUCCESS_REEQUIP_FAILED,
+ INVALID_PLAYER,
+ PLAYER_OPERATING,
+ PLAYER_TRADING,
+ PLAYER_DEAD,
+ PLAYER_PARALYZED,
+ PLAYER_FISHING,
+ PLAYER_SITTING,
+ CURSED_WEAPON_EQUIPPED,
+ TARGET_MISSING,
+ TARGET_NOT_OWNED,
+ TARGET_ALREADY_AUGMENTED,
+ TARGET_HERO_ITEM,
+ TARGET_SHADOW_ITEM,
+ TARGET_LOW_GRADE,
+ TARGET_INVALID_LOCATION,
+ TARGET_NOT_WEAPON,
+ TARGET_FISTS,
+ TARGET_FISHING_ROD,
+ TARGET_UNEQUIP_FAILED,
+ TARGET_STATE_CHANGED,
+ LIFE_STONE_MISSING,
+ LIFE_STONE_NOT_OWNED,
+ LIFE_STONE_INVALID_LOCATION,
+ LIFE_STONE_UNKNOWN,
+ PLAYER_LEVEL_TOO_LOW,
+ GEMSTONE_MISSING,
+ GEMSTONE_NOT_OWNED,
+ GEMSTONE_INVALID_LOCATION,
+ GEMSTONE_WRONG_ITEM,
+ GEMSTONE_COUNT_MISMATCH,
+ GEMSTONE_INSUFFICIENT,
+ AUGMENTATION_GENERATION_FAILED,
+ LIFE_STONE_CONSUME_FAILED,
+ GEMSTONE_CONSUME_FAILED,
+ AUGMENTATION_APPLY_FAILED,
+ ROLLBACK_FAILED
+ }
+
+ private static class SingletonHolder
+ {
+ private static final AugmentationService INSTANCE = new AugmentationService();
+ }
+}
LifeStoneManager.java:
LifeStoneManager.java
----------------------------------------------------------------------------------------------------
import java.util.Map;
import java.util.Set;
+import net.sf.l2j.gameserver.model.item.LifeStone;
+
/**
- * LifeStoneManager by Atrein
+ * Stores the canonical Life Stone definitions used by every augmentation entry point.
+ * <p>
+ * By Atrein:
+ * Replaces duplicated Life Stone tables with one immutable registry shared by the
+ * standard refinement packets and the direct-use Life Stone handler.
*/
-public class LifeStoneManager {
- private static final Map<Integer, Integer> ITEM_GRADES = new HashMap<>();
- private static final Map<Integer, Integer> ITEM_LEVELS = new HashMap<>();
-
- static {
- // Grade 0 - Common
- ITEM_GRADES.put(8723, 0); ITEM_LEVELS.put(8723, 0);
- ITEM_GRADES.put(8724, 0); ITEM_LEVELS.put(8724, 1);
- ITEM_GRADES.put(8725, 0); ITEM_LEVELS.put(8725, 2);
- ITEM_GRADES.put(8726, 0); ITEM_LEVELS.put(8726, 3);
- ITEM_GRADES.put(8727, 0); ITEM_LEVELS.put(8727, 4);
- ITEM_GRADES.put(8728, 0); ITEM_LEVELS.put(8728, 5);
- ITEM_GRADES.put(8729, 0); ITEM_LEVELS.put(8729, 6);
- ITEM_GRADES.put(8730, 0); ITEM_LEVELS.put(8730, 7);
- ITEM_GRADES.put(8731, 0); ITEM_LEVELS.put(8731, 8);
- ITEM_GRADES.put(8732, 0); ITEM_LEVELS.put(8732, 9);
-
- // Grade 1 - Mid
- ITEM_GRADES.put(8733, 1); ITEM_LEVELS.put(8733, 0);
- ITEM_GRADES.put(8734, 1); ITEM_LEVELS.put(8734, 1);
- ITEM_GRADES.put(8735, 1); ITEM_LEVELS.put(8735, 2);
- ITEM_GRADES.put(8736, 1); ITEM_LEVELS.put(8736, 3);
- ITEM_GRADES.put(8737, 1); ITEM_LEVELS.put(8737, 4);
- ITEM_GRADES.put(8738, 1); ITEM_LEVELS.put(8738, 5);
- ITEM_GRADES.put(8739, 1); ITEM_LEVELS.put(8739, 6);
- ITEM_GRADES.put(8740, 1); ITEM_LEVELS.put(8740, 7);
- ITEM_GRADES.put(8741, 1); ITEM_LEVELS.put(8741, 8);
- ITEM_GRADES.put(8742, 1); ITEM_LEVELS.put(8742, 9);
-
- // Grade 2 - High
- ITEM_GRADES.put(8743, 2); ITEM_LEVELS.put(8743, 0);
- ITEM_GRADES.put(8744, 2); ITEM_LEVELS.put(8744, 1);
- ITEM_GRADES.put(8745, 2); ITEM_LEVELS.put(8745, 2);
- ITEM_GRADES.put(8746, 2); ITEM_LEVELS.put(8746, 3);
- ITEM_GRADES.put(8747, 2); ITEM_LEVELS.put(8747, 4);
- ITEM_GRADES.put(8748, 2); ITEM_LEVELS.put(8748, 5);
- ITEM_GRADES.put(8749, 2); ITEM_LEVELS.put(8749, 6);
- ITEM_GRADES.put(8750, 2); ITEM_LEVELS.put(8750, 7);
- ITEM_GRADES.put(8751, 2); ITEM_LEVELS.put(8751, 8);
- ITEM_GRADES.put(8752, 2); ITEM_LEVELS.put(8752, 9);
-
- // Grade 3 - Top
- ITEM_GRADES.put(8753, 3); ITEM_LEVELS.put(8753, 0);
- ITEM_GRADES.put(8754, 3); ITEM_LEVELS.put(8754, 1);
- ITEM_GRADES.put(8755, 3); ITEM_LEVELS.put(8755, 2);
- ITEM_GRADES.put(8756, 3); ITEM_LEVELS.put(8756, 3);
- ITEM_GRADES.put(8757, 3); ITEM_LEVELS.put(8757, 4);
- ITEM_GRADES.put(8758, 3); ITEM_LEVELS.put(8758, 5);
- ITEM_GRADES.put(8759, 3); ITEM_LEVELS.put(8759, 6);
- ITEM_GRADES.put(8760, 3); ITEM_LEVELS.put(8760, 7);
- ITEM_GRADES.put(8761, 3); ITEM_LEVELS.put(8761, 8);
- ITEM_GRADES.put(8762, 3); ITEM_LEVELS.put(8762, 9);
- }
-
- public static int getGrade(int itemId) {
- return ITEM_GRADES.getOrDefault(itemId, 0); // по умолчанию
- }
-
- public static int getLevel(int itemId) {
- return ITEM_LEVELS.getOrDefault(itemId, 0); // по умолчанию
- }
-
- public static boolean isLifeStone(int itemId) {
- return ITEM_GRADES.containsKey(itemId);
- }
-
- public static Set<Integer> getAllIds() {
- return ITEM_GRADES.keySet();
- }
-
- public static int[] getAllIdsArray() {
- return ITEM_GRADES.keySet().stream().mapToInt(Integer::intValue).toArray();
- }
+public final class LifeStoneManager
+{
+ private static final int FIRST_ITEM_ID = 8723;
+ private static final int LEVELS_PER_GRADE = 10;
+ private static final int GRADE_COUNT = 4;
+
+ private static final Map<Integer, LifeStone> LIFE_STONES = buildLifeStones();
+
+ private LifeStoneManager()
+ {
+ }
+
+ /**
+ * @param itemId The Life Stone item ID.
+ * @return The immutable Life Stone definition, or {@code null} when the item is not a Life Stone.
+ */
+ public static LifeStone getLifeStone(int itemId)
+ {
+ return LIFE_STONES.get(itemId);
+ }
+
+ /**
+ * @param itemId The item ID to inspect.
+ * @return {@code true} when the item is a registered Life Stone.
+ */
+ public static boolean isLifeStone(int itemId)
+ {
+ return LIFE_STONES.containsKey(itemId);
+ }
+
+ /**
+ * @return An immutable view of every registered Life Stone item ID.
+ */
+ public static Set<Integer> getItemIds()
+ {
+ return LIFE_STONES.keySet();
+ }
+
+ private static Map<Integer, LifeStone> buildLifeStones()
+ {
+ final Map<Integer, LifeStone> lifeStones = new HashMap<>(GRADE_COUNT * LEVELS_PER_GRADE);
+ int itemId = FIRST_ITEM_ID;
+
+ for (int grade = 0; grade < GRADE_COUNT; grade++)
+ {
+ for (int level = 0; level < LEVELS_PER_GRADE; level++, itemId++)
+ lifeStones.put(itemId, new LifeStone(grade, level));
+ }
+ return Map.copyOf(lifeStones);
+ }
}
AbstractRefinePacket.java:
AbstractRefinePacket.java
----------------------------------------------------------------------------------------------------
package net.sf.l2j.gameserver.network.clientpackets;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.sf.l2j.gameserver.enums.items.CrystalType;
-import net.sf.l2j.gameserver.enums.items.ItemLocation;
-import net.sf.l2j.gameserver.enums.items.WeaponType;
+import net.sf.l2j.gameserver.data.LifeStoneManager;
import net.sf.l2j.gameserver.model.actor.Player;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService.ValidationStatus;
import net.sf.l2j.gameserver.model.item.LifeStone;
import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
-import net.sf.l2j.gameserver.model.item.kind.Weapon;
-import net.sf.l2j.gameserver.network.SystemMessageId;
public abstract class AbstractRefinePacket extends L2GameClientPacket
{
public static final int GRADE_HIGH = 2;
public static final int GRADE_TOP = 3;
- private static final Map<Integer, LifeStone> _lifeStones = new HashMap<>();
-
- static
+ /**
+ * By Atrein:
+ * Uses the canonical Life Stone registry shared by every augmentation path.
+ * @param itemId The Life Stone item ID.
+ * @return The Life Stone definition, or {@code null} for an unsupported item.
+ */
+ protected static LifeStone getLifeStone(int itemId)
{
- int itemId = 8723;
-
- // Populate GRADE_NONE life stones
- for (int i = 0; i < 10; i++, itemId++)
- _lifeStones.put(itemId, new LifeStone(GRADE_NONE, i));
-
- // Populate GRADE_MID life stones
- for (int i = 0; i < 10; i++, itemId++)
- _lifeStones.put(itemId, new LifeStone(GRADE_MID, i));
-
- // Populate GRADE_HIGH life stones
- for (int i = 0; i < 10; i++, itemId++)
- _lifeStones.put(itemId, new LifeStone(GRADE_HIGH, i));
-
- // Populate GRADE_TOP life stones
- for (int i = 0; i < 10; i++, itemId++)
- _lifeStones.put(itemId, new LifeStone(GRADE_TOP, i));
- }
-
- protected static final LifeStone getLifeStone(int itemId)
- {
- return _lifeStones.get(itemId);
+ return LifeStoneManager.getLifeStone(itemId);
}
/**
- * @param player : The {@link Player} to test.
- * @param item : The {@link ItemInstance} to test.
- * @param refinerItem : The {@link ItemInstance} used as Lifestone to test.
- * @param gemstoneItem : The {@link ItemInstance} used as gemstone to test.
- * @return True if the augmentation process is doable, otherwise return false.
+ * @param player The player to validate.
+ * @param item The target item.
+ * @param refinerItem The Life Stone.
+ * @param gemstoneItem The gemstone stack.
+ * @return {@code true} when the complete augmentation request is valid.
*/
- protected static final boolean isValid(Player player, ItemInstance item, ItemInstance refinerItem, ItemInstance gemstoneItem)
+ protected static boolean isValid(Player player, ItemInstance item, ItemInstance refinerItem, ItemInstance gemstoneItem)
{
- // Conditions must be valid.
- if (!isValid(player, item, refinerItem))
- return false;
-
- // Gemstones must belong to owner.
- if (gemstoneItem.getOwnerId() != player.getObjectId())
- return false;
-
- // Gemstones must be in inventory.
- if (gemstoneItem.getLocation() != ItemLocation.INVENTORY)
- return false;
-
- final CrystalType grade = item.getItem().getCrystalType();
-
- // Check for item id.
- if (grade.getGemstoneId() != gemstoneItem.getItemId())
- return false;
-
- // Existing count must be greater or equal required number.
- return gemstoneItem.getCount() >= grade.getGemstoneCount();
+ final int gemstoneCount = item == null ? 0 : item.getItem().getCrystalType().getGemstoneCount();
+ return validateAndNotify(player, AugmentationService.getInstance().validate(player, item, refinerItem, gemstoneItem, gemstoneCount));
}
/**
- * @param player : The {@link Player} to test.
- * @param item : The {@link ItemInstance} to test.
- * @param refinerItem : The {@link ItemInstance} used as Lifestone to test.
- * @return True if the conditions for augmentation process are met for the {@link Player}, the {@link ItemInstance} and the {@link ItemInstance} used as Lifestone set as parameters.
+ * @param player The player to validate.
+ * @param item The target item.
+ * @param refinerItem The Life Stone.
+ * @return {@code true} when the target and Life Stone are valid.
*/
- protected static final boolean isValid(Player player, ItemInstance item, ItemInstance refinerItem)
+ protected static boolean isValid(Player player, ItemInstance item, ItemInstance refinerItem)
{
- if (refinerItem == null)
- return false;
-
- // Both Player and item conditions must be valid.
- if (!isValid(player, item))
- return false;
-
- // The Lifestone must belong to owner.
- if (refinerItem.getOwnerId() != player.getObjectId())
- return false;
-
- // The Lifestone must be in inventory.
- if (refinerItem.getLocation() != ItemLocation.INVENTORY)
- return false;
-
- final LifeStone ls = _lifeStones.get(refinerItem.getItemId());
- if (ls == null)
- return false;
-
- // The level of the Player must be higher than the level of the Lifestone.
- if (player.getStatus().getLevel() < ls.getPlayerLevel())
- {
- player.sendPacket(SystemMessageId.HARDENER_LEVEL_TOO_HIGH);
- return false;
- }
-
- return true;
+ return validateAndNotify(player, AugmentationService.getInstance().validateLifeStone(player, item, refinerItem));
}
/**
- * @param player : The {@link Player} to test.
- * @param item : The {@link ItemInstance} to test.
- * @return True if the conditions for augmentation process are met for the {@link Player} and the {@link ItemInstance} set as parameters.
+ * @param player The player to validate.
+ * @param item The target item.
+ * @return {@code true} when the player and target item are valid.
*/
- protected static final boolean isValid(Player player, ItemInstance item)
+ protected static boolean isValid(Player player, ItemInstance item)
{
- if (item == null)
- return false;
-
- // The Player conditions must be valid.
- if (!isValid(player))
- return false;
-
- // The item must belong to owner.
- if (item.getOwnerId() != player.getObjectId() || item.isAugmented() || item.isHeroItem() || item.isShadowItem() || item.getItem().getCrystalType().isLesser(CrystalType.C))
- return false;
-
- // The item must be in inventory, but not equipped.
- if (item.getLocation() != ItemLocation.INVENTORY && item.getLocation() != ItemLocation.PAPERDOLL)
- return false;
-
- // The item must be a Weapon.
- if (!(item.getItem() instanceof Weapon weapon))
- return false;
-
- // Rods and fists aren't augmentable.
- return weapon.getItemType() != WeaponType.NONE && weapon.getItemType() != WeaponType.FISHINGROD;
+ return validateAndNotify(player, AugmentationService.getInstance().validateTarget(player, item));
}
/**
- * @param player : The {@link Player} to test.
- * @return True if the conditions for augmentation process are met for the {@link Player} set as parameter.
+ * @param player The player to validate.
+ * @return {@code true} when the player can use augmentation services.
*/
- protected static final boolean isValid(Player player)
+ protected static boolean isValid(Player player)
{
- if (player == null)
- return false;
-
- if (player.isOperating())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_A_PRIVATE_STORE_OR_PRIVATE_WORKSHOP_IS_IN_OPERATION);
- return false;
- }
-
- if (player.isProcessingTransaction())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_TRADING);
- return false;
- }
-
- if (player.isDead())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_DEAD);
- return false;
- }
-
- if (player.isParalyzed())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_PARALYZED);
- return false;
- }
-
- if (player.isFishing())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_FISHING);
- return false;
- }
-
- if (player.isSitting())
- {
- player.sendPacket(SystemMessageId.YOU_CANNOT_AUGMENT_ITEMS_WHILE_SITTING_DOWN);
- return false;
- }
-
- return !player.isCursedWeaponEquipped();
+ return validateAndNotify(player, AugmentationService.getInstance().validatePlayer(player));
+ }
+
+ private static boolean validateAndNotify(Player player, ValidationStatus status)
+ {
+ if (status == ValidationStatus.SUCCESS)
+ return true;
+ AugmentationService.getInstance().sendStandardFailure(player, status);
+ return false;
}
}
RequestRefine.java:
RequestRefine.java
----------------------------------------------------------------------------------------------------
package net.sf.l2j.gameserver.network.clientpackets;
-import net.sf.l2j.gameserver.data.xml.AugmentationData;
-import net.sf.l2j.gameserver.enums.ShortcutType;
-import net.sf.l2j.gameserver.enums.actors.MissionType;
-import net.sf.l2j.gameserver.model.Augmentation;
import net.sf.l2j.gameserver.model.actor.Player;
-import net.sf.l2j.gameserver.model.item.LifeStone;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService;
+import net.sf.l2j.gameserver.model.enchant.AugmentationService.OperationResult;
import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
import net.sf.l2j.gameserver.network.SystemMessageId;
import net.sf.l2j.gameserver.network.serverpackets.ExVariationResult;
return;
final ItemInstance targetItem = player.getInventory().getItemByObjectId(_targetItemObjId);
- if (targetItem == null)
+ final ItemInstance refinerItem = player.getInventory().getItemByObjectId(_refinerItemObjId);
+ final ItemInstance gemstoneItem = player.getInventory().getItemByObjectId(_gemStoneItemObjId);
+
+ // By Atrein: standard refinement and direct-use Life Stones share the same
+ // validation, charging, rollback and persistence workflow.
+ final OperationResult result = AugmentationService.getInstance().augment(player, targetItem, refinerItem, gemstoneItem, _gemStoneCount, AugmentationService.AugmentationMode.STANDARD);
+ if (!result.isSuccess())
{
player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
+ if (!AugmentationService.getInstance().sendStandardFailure(player, result.status()))
+ player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
return;
}
- final ItemInstance refinerItem = player.getInventory().getItemByObjectId(_refinerItemObjId);
- if (refinerItem == null)
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- final ItemInstance gemStoneItem = player.getInventory().getItemByObjectId(_gemStoneItemObjId);
- if (gemStoneItem == null)
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- if (!isValid(player, targetItem, refinerItem, gemStoneItem))
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- final LifeStone ls = getLifeStone(refinerItem.getItemId());
- if (ls == null)
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- if (_gemStoneCount != targetItem.getItem().getCrystalType().getGemstoneCount())
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- // unequip item
- if (targetItem.isEquipped())
- {
- player.getInventory().unequipItemInSlotAndRecord(targetItem.getLocationSlot());
- player.broadcastUserInfo();
- }
-
- // Consume the life stone
- if (!player.destroyItem(refinerItem, 1, false))
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- // Consume gemstones
- if (!player.destroyItem(gemStoneItem, _gemStoneCount, false))
- {
- player.sendPacket(ExVariationResult.RESULT_FAILED);
- player.sendPacket(SystemMessageId.AUGMENTATION_FAILED_DUE_TO_INAPPROPRIATE_CONDITIONS);
- return;
- }
-
- final Augmentation aug = AugmentationData.getInstance().generateRandomAugmentation(ls.getLevel(), ls.getGrade());
- targetItem.setAugmentation(aug, player);
-
- final int stat12 = 0x0000FFFF & aug.getId();
- final int stat34 = aug.getId() >> 16;
- player.sendPacket(new ExVariationResult(stat12, stat34, 1));
-
- // Refresh shortcuts.
- player.getShortcutList().refreshShortcuts(s -> targetItem.getObjectId() == s.getId() && s.getType() == ShortcutType.ITEM);
- player.getMissions().update(MissionType.AUGMENT);
+ final int augmentationId = result.augmentation().getId();
+ player.sendPacket(new ExVariationResult(augmentationId & 0x0000FFFF, augmentationId >> 16, 1));
}
}
en_US/sysstring.xml:
en_US/sysstring.xml
----------------------------------------------------------------------------------------------------
<string key="10276">Enchant boost does not apply to this scroll and remains active.</string>
<string key="10277">The enchant chance is already 100%%. Your +%s%% boost remains active.</string>
<string key="10278">Your active enchant boost +%s%% has been restored.</string>
+ <!-- gameserver.handler.itemhandlers.LifestoneHandler && gameserver.model.enchant.AugmentationService -->
+ <string key="10279">Equip a weapon before using a Life Stone.</string>
+ <string key="10280">This item is not a supported Life Stone.</string>
+ <string key="10281">Only C-S grade weapons can be augmented.</string>
+ <string key="10282">This weapon cannot be augmented.</string>
+ <string key="10283">This weapon is already augmented.</string>
+ <string key="10284">You need %s %s for this augmentation.</string>
+ <string key="10285">The augmentation could not be generated. Please try again.</string>
+ <string key="10286">Augmentation failed. Any consumed materials were returned.</string>
+ <string key="10287">Augmentation failed and not all consumed materials could be returned. Contact an administrator.</string>
+ <string key="10288">The augmentation was successful.</string>
+ <string key="10289">The augmentation was successful, but the weapon could not be re-equipped. Equip it manually.</string>
+ <string key="10290">The weapon state changed before augmentation. No materials were consumed.</string>
+
</list>
ru_RU/sysstring.xml:
ru_RU/sysstring.xml
----------------------------------------------------------------------------------------------------
<string key="10276">Этот свиток не поддерживает бонус заточки. Бонус сохранён.</string>
<string key="10277">Шанс заточки уже равен 100%%. Бонус +%s%% сохранён.</string>
<string key="10278">Активный бонус заточки +%s%% восстановлен после входа.</string>
+ <!-- gameserver.handler.itemhandlers.LifestoneHandler && gameserver.model.enchant.AugmentationService -->
+ <string key="10279">Перед использованием Камня Жизни экипируйте оружие.</string>
+ <string key="10280">Этот предмет не является поддерживаемым Камнем Жизни.</string>
+ <string key="10281">Можно аугментировать только оружие C-S грейда.</string>
+ <string key="10282">Это оружие нельзя аугментировать.</string>
+ <string key="10283">Это оружие уже аугментировано.</string>
+ <string key="10284">Для аугментации требуется %s %s.</string>
+ <string key="10285">Не удалось создать аугментацию. Повторите попытку.</string>
+ <string key="10286">Аугментация не выполнена. Все списанные материалы возвращены.</string>
+ <string key="10287">Аугментация не выполнена, и не все материалы удалось вернуть. Обратитесь к администратору.</string>
+ <string key="10288">Аугментация успешно выполнена.</string>
+ <string key="10289">Аугментация выполнена, но оружие не удалось экипировать обратно. Экипируйте его вручную.</string>
+ <string key="10290">Состояние оружия изменилось до начала аугментации. Материалы не списаны.</string>
+
</list>