Welcome to RUSaCis - эмулятор Interlude

Присоединяйтесь к нам прямо сейчас, чтобы получить доступ ко всем нашим возможностям. После регистрации и входа в систему вы сможете создавать темы, публиковать ответы в существующих темах, давать репутацию пользователям - так же приобрести исходный код. Это также быстро, так чего же вы ждете?

LifeStone's по клику v2.0 - RusAcis v3.8

Atrein

Вассал
INTERLUDE
INTERFACE
Регистрация
16 Янв 2022
Сообщения
71
Реакции
38
Баллы
18
RaCoin
5
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>
 

Cringe

Вассал
INTERLUDE
INTERLUDE
INTERFACE
Регистрация
5 Мар 2025
Сообщения
52
Реакции
8
Баллы
8
RaCoin
65
Как я понял, можно будет кликать на сам камень в любом месте на карте, и будет открываться окно со вставкой?
 

Atrein

Вассал
INTERLUDE
INTERFACE
Регистрация
16 Янв 2022
Сообщения
71
Реакции
38
Баллы
18
RaCoin
5
Как я понял, можно будет кликать на сам камень в любом месте на карте, и будет открываться окно со вставкой?
Да, теперь Life Stone можно использовать прямо из инвентаря в любом месте, без NPC.

Нужно просто:
  1. Надеть подходящее оружие.
  2. Иметь в инвентаре Life Stone и нужные Gemstones.
  3. Нажать на Life Stone.

Если все условия выполнены, сервер сам вставит аугментацию в оружие. Отдельное окно открываться не будет.
 

Atrein

Вассал
INTERLUDE
INTERFACE
Регистрация
16 Янв 2022
Сообщения
71
Реакции
38
Баллы
18
RaCoin
5
В общем работает так:
  1. Персонаж экипирует подходящее оружие C–S Grade.
  2. В инвентаре должны быть Life Stone и нужное количество Gemstones.
  3. Игрок дважды кликает по Life Stone.
  4. Сервер проверяет все условия, снимает оружие, списывает материалы, устанавливает аугментацию и надевает оружие обратно.
Использовать камень можно без NPC практически в любом месте мира, но операция не сработает, если персонаж:
  • мёртв, сидит, рыбачит или парализован;
  • торгует либо открыл частный магазин;
  • использует проклятое оружие;
  • держит Hero/Shadow Weapon, удочку или оружие ниже C Grade;
  • уже имеет аугментацию;
  • не соответствует уровню Life Stone;
  • не имеет нужного количества Gemstones.
 
Сверху Снизу