Jump to content

Recommended Posts

Hi, I have a sash with scale system, what do I need to do to be able to add normal  bonuses to sash?
Currently, when I set eg max hp, strong against human etc, the bonuses are not added to the character, why?

- my CItem::ModifyPoints function in item.cpp: 

 

#include "stdafx.h"
#include "utils.h"
#include "config.h"
#include "char.h"
#include "desc.h"
#include "sectree_manager.h"
#include "packet.h"
#include "protocol.h"
#include "log.h"
#include "skill.h"
#include "unique_item.h"
#include "profiler.h"
#include "marriage.h"
#include "item_addon.h"
#include "dev_log.h"
#include "locale_service.h"
#include "item.h"
#include "item_manager.h"
#include "affect.h"
#include "DragonSoul.h"
#include "buff_on_attributes.h"
#include "belt_inventory_helper.h"
#include "../../common/VnumHelper.h"

CItem::CItem(DWORD dwVnum)
    : m_dwVnum(dwVnum), m_bWindow(0), m_dwID(0), m_bEquipped(false), m_dwVID(0), m_wCell(0), m_dwCount(0), m_lFlag(0), m_dwLastOwnerPID(0),
    m_bExchanging(false), m_pkDestroyEvent(NULL), m_pkUniqueExpireEvent(NULL), m_pkTimerBasedOnWearExpireEvent(NULL), m_pkRealTimeExpireEvent(NULL),
    m_pkExpireEvent(NULL),
       m_pkAccessorySocketExpireEvent(NULL), m_pkOwnershipEvent(NULL), m_dwOwnershipPID(0), m_bSkipSave(false), m_isLocked(false),
    m_dwMaskVnum(0), m_dwSIGVnum (0)
{
    memset( &m_alSockets, 0, sizeof(m_alSockets) );
    memset( &m_aAttr, 0, sizeof(m_aAttr) );
}

CItem::~CItem()
{
    Destroy();
}

void CItem::Initialize()
{
    CEntity::Initialize(ENTITY_ITEM);

    m_bWindow = RESERVED_WINDOW;
    m_pOwner = NULL;
    m_dwID = 0;
    m_bEquipped = false;
    m_dwVID = m_wCell = m_dwCount = m_lFlag = 0;
    m_pProto = NULL;
    m_bExchanging = false;
    memset(&m_alSockets, 0, sizeof(m_alSockets));
    memset(&m_aAttr, 0, sizeof(m_aAttr));

    m_pkDestroyEvent = NULL;
    m_pkOwnershipEvent = NULL;
    m_dwOwnershipPID = 0;
    m_pkUniqueExpireEvent = NULL;
    m_pkTimerBasedOnWearExpireEvent = NULL;
    m_pkRealTimeExpireEvent = NULL;

    m_pkAccessorySocketExpireEvent = NULL;

    m_bSkipSave = false;
    m_dwLastOwnerPID = 0;
}

void CItem::Destroy()
{
    event_cancel(&m_pkDestroyEvent);
    event_cancel(&m_pkOwnershipEvent);
    event_cancel(&m_pkUniqueExpireEvent);
    event_cancel(&m_pkTimerBasedOnWearExpireEvent);
    event_cancel(&m_pkRealTimeExpireEvent);
    event_cancel(&m_pkAccessorySocketExpireEvent);

    CEntity::Destroy();

    if (GetSectree())
        GetSectree()->RemoveEntity(this);
}

EVENTFUNC(item_destroy_event)
{
    item_event_info* info = dynamic_cast<item_event_info*>( event->info );

    if ( info == NULL )
    {
        sys_err( "item_destroy_event> <Factor> Null pointer" );
        return 0;
    }

    LPITEM pkItem = info->item;

    if (pkItem->GetOwner())
        sys_err("item_destroy_event: Owner exist. (item %s owner %s)", pkItem->GetName(), pkItem->GetOwner()->GetName());

    pkItem->SetDestroyEvent(NULL);
    M2_DESTROY_ITEM(pkItem);
    return 0;
}

void CItem::SetDestroyEvent(LPEVENT pkEvent)
{
    m_pkDestroyEvent = pkEvent;
}

void CItem::StartDestroyEvent(int iSec)
{
    if (m_pkDestroyEvent)
        return;

    item_event_info* info = AllocEventInfo<item_event_info>();
    info->item = this;

    SetDestroyEvent(event_create(item_destroy_event, info, PASSES_PER_SEC(iSec)));
}

void CItem::EncodeInsertPacket(LPENTITY ent)
{
    LPDESC d;

    if (!(d = ent->GetDesc()))
        return;

    const PIXEL_POSITION & c_pos = GetXYZ();

    struct packet_item_ground_add pack;

    pack.bHeader    = HEADER_GC_ITEM_GROUND_ADD;
    pack.x        = c_pos.x;
    pack.y        = c_pos.y;
    pack.z        = c_pos.z;
    pack.dwVnum        = GetVnum();
    pack.dwVID        = m_dwVID;
    //pack.count    = m_dwCount;

    d->Packet(&pack, sizeof(pack));

    if (m_pkOwnershipEvent != NULL)
    {
        item_event_info * info = dynamic_cast<item_event_info *>(m_pkOwnershipEvent->info);

        if ( info == NULL )
        {
            sys_err( "CItem::EncodeInsertPacket> <Factor> Null pointer" );
            return;
        }

        TPacketGCItemOwnership p;

        p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
        p.dwVID = m_dwVID;
        strlcpy(p.szName, info->szOwnerName, sizeof(p.szName));

        d->Packet(&p, sizeof(TPacketGCItemOwnership));
    }
}

void CItem::EncodeRemovePacket(LPENTITY ent)
{
    LPDESC d;

    if (!(d = ent->GetDesc()))
        return;

    struct packet_item_ground_del pack;

    pack.bHeader    = HEADER_GC_ITEM_GROUND_DEL;
    pack.dwVID        = m_dwVID;

    d->Packet(&pack, sizeof(pack));
    sys_log(2, "Item::EncodeRemovePacket %s to %s", GetName(), ((LPCHARACTER) ent)->GetName());
}

void CItem::SetProto(const TItemTable * table)
{
    assert(table != NULL);
    m_pProto = table;
    SetFlag(m_pProto->dwFlags);
}

void CItem::UsePacketEncode(LPCHARACTER ch, LPCHARACTER victim, struct packet_item_use *packet)
{
    if (!GetVnum())
        return;

    packet->header     = HEADER_GC_ITEM_USE;
    packet->ch_vid     = ch->GetVID();
    packet->victim_vid     = victim->GetVID();
    packet->Cell = TItemPos(GetWindow(), m_wCell);
    packet->vnum    = GetVnum();
}

void CItem::RemoveFlag(long bit)
{
    REMOVE_BIT(m_lFlag, bit);
}

void CItem::AddFlag(long bit)
{
    SET_BIT(m_lFlag, bit);
}

void CItem::UpdatePacket()
{
    if (!m_pOwner || !m_pOwner->GetDesc())
        return;

    TPacketGCItemUpdate pack;

    pack.header = HEADER_GC_ITEM_UPDATE;
    pack.Cell = TItemPos(GetWindow(), m_wCell);
    pack.count    = m_dwCount;

    for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
        pack.alSockets[i] = m_alSockets[i];

    thecore_memcpy(pack.aAttr, GetAttributes(), sizeof(pack.aAttr));

    sys_log(2, "UpdatePacket %s -> %s", GetName(), m_pOwner->GetName());
    m_pOwner->GetDesc()->Packet(&pack, sizeof(pack));
}

DWORD CItem::GetCount()
{
    if (GetType() == ITEM_ELK) return MIN(m_dwCount, INT_MAX);
    else
    {
        return MIN(m_dwCount, 200);
    }
}

bool CItem::SetCount(DWORD count)
{
    if (GetType() == ITEM_ELK)
    {
        m_dwCount = MIN(count, INT_MAX);
    }
    else
    {
        m_dwCount = MIN(count, ITEM_MAX_COUNT);
    }

    if (count == 0 && m_pOwner)
    {
        if (GetSubType() == USE_ABILITY_UP || GetSubType() == USE_POTION || GetVnum() == 70020)
        {
            LPCHARACTER pOwner = GetOwner();
            WORD wCell = GetCell();

            RemoveFromCharacter();

            if (!IsDragonSoul())
            {
                LPITEM pItem = pOwner->FindSpecifyItem(GetVnum());

                if (NULL != pItem)
                {
                    pOwner->ChainQuickslotItem(pItem, QUICKSLOT_TYPE_ITEM, wCell);
                }
                else
                {
                    pOwner->SyncQuickslot(QUICKSLOT_TYPE_ITEM, wCell, 255);
                }
            }

            M2_DESTROY_ITEM(this);
        }
        else
        {
            if (!IsDragonSoul())
            {
                m_pOwner->SyncQuickslot(QUICKSLOT_TYPE_ITEM, m_wCell, 255);
            }
            M2_DESTROY_ITEM(RemoveFromCharacter());
        }

        return false;
    }

    UpdatePacket();

    Save();
    return true;
}

LPITEM CItem::RemoveFromCharacter()
{
    if (!m_pOwner)
    {
        sys_err("Item::RemoveFromCharacter owner null");
        return (this);
    }

    LPCHARACTER pOwner = m_pOwner;

    if (m_bEquipped)    // 장착되었는가?
    {
        Unequip();
        //pOwner->UpdatePacket();

        SetWindow(RESERVED_WINDOW);
        Save();
        return (this);
    }
    else
    {
        if (GetWindow() != SAFEBOX && GetWindow() != MALL)
        {
            if (IsDragonSoul())
            {
                if (m_wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
                    sys_err("CItem::RemoveFromCharacter: pos >= DRAGON_SOUL_INVENTORY_MAX_NUM");
                else
                    pOwner->SetItem(TItemPos(m_bWindow, m_wCell), NULL);
            }
            else
            {
                TItemPos cell(INVENTORY, m_wCell);

                if (false == cell.IsDefaultInventoryPosition() && false == cell.IsBeltInventoryPosition()) // 아니면 소지품에?
                    sys_err("CItem::RemoveFromCharacter: Invalid Item Position");
                else
                {
                    pOwner->SetItem(cell, NULL);
                }
            }
        }

        m_pOwner = NULL;
        m_wCell = 0;

        SetWindow(RESERVED_WINDOW);
        Save();
        return (this);
    }
}

bool CItem::AddToCharacter(LPCHARACTER ch, TItemPos Cell)
{
    assert(GetSectree() == NULL);
    assert(m_pOwner == NULL);
    WORD pos = Cell.cell;
    BYTE window_type = Cell.window_type;
    
    if (INVENTORY == window_type)
    {
        if (m_wCell >= INVENTORY_MAX_NUM && BELT_INVENTORY_SLOT_START > m_wCell)
        {
            sys_err("CItem::AddToCharacter: cell overflow: %s to %s cell %d", m_pProto->szName, ch->GetName(), m_wCell);
            return false;
        }
    }
    else if (DRAGON_SOUL_INVENTORY == window_type)
    {
        if (m_wCell >= DRAGON_SOUL_INVENTORY_MAX_NUM)
        {
            sys_err("CItem::AddToCharacter: cell overflow: %s to %s cell %d", m_pProto->szName, ch->GetName(), m_wCell);
            return false;
        }
    }

    if (ch->GetDesc())
        m_dwLastOwnerPID = ch->GetPlayerID();

    event_cancel(&m_pkDestroyEvent);

    ch->SetItem(TItemPos(window_type, pos), this);
    m_pOwner = ch;

    Save();
    return true;
}

LPITEM CItem::RemoveFromGround()
{
    if (GetSectree())
    {
        SetOwnership(NULL);
        
        GetSectree()->RemoveEntity(this);
        
        ViewCleanup();

        Save();
    }

    return (this);
}

bool CItem::AddToGround(long lMapIndex, const PIXEL_POSITION & pos, bool skipOwnerCheck)
{
    if (0 == lMapIndex)
    {
        sys_err("wrong map index argument: %d", lMapIndex);
        return false;
    }

    if (GetSectree())
    {
        sys_err("sectree already assigned");
        return false;
    }

    if (!skipOwnerCheck && m_pOwner)
    {
        sys_err("owner pointer not null");
        return false;
    }

    LPSECTREE tree = SECTREE_MANAGER::instance().Get(lMapIndex, pos.x, pos.y);

    if (!tree)
    {
        sys_err("cannot find sectree by %dx%d", pos.x, pos.y);
        return false;
    }

    //tree->Touch();

    SetWindow(GROUND);
    SetXYZ(pos.x, pos.y, pos.z);
    tree->InsertEntity(this);
    UpdateSectree();
    Save();
    return true;
}

bool CItem::DistanceValid(LPCHARACTER ch)
{
    if (!GetSectree())
        return false;

    int iDist = DISTANCE_APPROX(GetX() - ch->GetX(), GetY() - ch->GetY());

    if (iDist > 300)
        return false;

    return true;
}

bool CItem::CanUsedBy(LPCHARACTER ch)
{
    // Anti flag check
    switch (ch->GetJob())
    {
        case JOB_WARRIOR:
            if (GetAntiFlag() & ITEM_ANTIFLAG_WARRIOR)
                return false;
            break;

        case JOB_ASSASSIN:
            if (GetAntiFlag() & ITEM_ANTIFLAG_ASSASSIN)
                return false;
            break;

        case JOB_SHAMAN:
            if (GetAntiFlag() & ITEM_ANTIFLAG_SHAMAN)
                return false;
            break;

        case JOB_SURA:
            if (GetAntiFlag() & ITEM_ANTIFLAG_SURA)
                return false;
            break;
    }

    return true;
}

int CItem::FindEquipCell(LPCHARACTER ch, int iCandidateCell)
{
    // 코스츔 아이템(ITEM_COSTUME)은 WearFlag 없어도 됨. (sub type으로 착용위치 구분. 귀찮게 또 wear flag 줄 필요가 있나..)
    // 용혼석(ITEM_DS, ITEM_SPECIAL_DS)도  SUB_TYPE으로 구분. 신규 반지, 벨트는 ITEM_TYPE으로 구분 -_-
    if ((0 == GetWearFlag() || ITEM_TOTEM == GetType()) && ITEM_COSTUME != GetType() && ITEM_DS != GetType() && ITEM_SPECIAL_DS != GetType() && ITEM_RING != GetType() && ITEM_BELT != GetType())
        return -1;

    // 용혼석 슬롯을 WEAR로 처리할 수가 없어서(WEAR는 최대 32개까지 가능한데 용혼석을 추가하면 32가 넘는다.)
    // 인벤토리의 특정 위치((INVENTORY_MAX_NUM + WEAR_MAX_NUM)부터 (INVENTORY_MAX_NUM + WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX - 1)까지)를
    // 용혼석 슬롯으로 정함.
    // return 할 때에, INVENTORY_MAX_NUM을 뺀 이유는,
    // 본래 WearCell이 INVENTORY_MAX_NUM를 빼고 return 하기 때문.
    if (GetType() == ITEM_DS || GetType() == ITEM_SPECIAL_DS)
    {
        if (iCandidateCell < 0)
        {
            return WEAR_MAX_NUM + GetSubType();
        }
        else
        {
            for (int i = 0; i < DRAGON_SOUL_DECK_MAX_NUM; i++)
            {
                if (WEAR_MAX_NUM + i * DS_SLOT_MAX + GetSubType() == iCandidateCell)
                {
                    return iCandidateCell;
                }
            }
            return -1;
        }
    }
    else if (GetType() == ITEM_COSTUME)
    {
        if (GetSubType() == COSTUME_BODY)
            return WEAR_COSTUME_BODY;
        else if (GetSubType() == COSTUME_HAIR)
            return WEAR_COSTUME_HAIR;
#ifdef __WEAPON_COSTUME_SYSTEM__
        else if (GetSubType() == COSTUME_WEAPON)
            return WEAR_COSTUME_WEAPON;
#endif
    }
    else if (GetType() == ITEM_RING)
    {
        if (ch->GetWear(WEAR_RING1))
            return WEAR_RING2;
        else
            return WEAR_RING1;
    }
    else if (GetType() == ITEM_BELT)
        return WEAR_BELT;
    else if (GetWearFlag() & WEARABLE_BODY)
        return WEAR_BODY;
    else if (GetWearFlag() & WEARABLE_HEAD)
        return WEAR_HEAD;
    else if (GetWearFlag() & WEARABLE_FOOTS)
        return WEAR_FOOTS;
    else if (GetWearFlag() & WEARABLE_WRIST)
        return WEAR_WRIST;
    else if (GetWearFlag() & WEARABLE_WEAPON)
        return WEAR_WEAPON;
    else if (GetWearFlag() & WEARABLE_SHIELD)
        return WEAR_SHIELD;
    else if (GetWearFlag() & WEARABLE_NECK)
        return WEAR_NECK;
    else if (GetWearFlag() & WEARABLE_EAR)
        return WEAR_EAR;
    else if (GetWearFlag() & WEARABLE_ARROW)
        return WEAR_ARROW;
    else if (GetWearFlag() & WEARABLE_UNIQUE)
    {
        if (ch->GetWear(WEAR_UNIQUE1))
            return WEAR_UNIQUE2;
        else
            return WEAR_UNIQUE1;
    }

    // 수집 퀘스트를 위한 아이템이 박히는곳으로 한번 박히면 절대 E수 없다.
    else if (GetWearFlag() & WEARABLE_ABILITY)
    {
        if (!ch->GetWear(WEAR_ABILITY1))
        {
            return WEAR_ABILITY1;
        }
        else if (!ch->GetWear(WEAR_ABILITY2))
        {
            return WEAR_ABILITY2;
        }
        else if (!ch->GetWear(WEAR_ABILITY3))
        {
            return WEAR_ABILITY3;
        }
        else if (!ch->GetWear(WEAR_ABILITY4))
        {
            return WEAR_ABILITY4;
        }
        else if (!ch->GetWear(WEAR_ABILITY5))
        {
            return WEAR_ABILITY5;
        }
        else if (!ch->GetWear(WEAR_ABILITY6))
        {
            return WEAR_ABILITY6;
        }
        else if (!ch->GetWear(WEAR_ABILITY7))
        {
            return WEAR_ABILITY7;
        }
        else if (!ch->GetWear(WEAR_ABILITY8))
        {
            return WEAR_ABILITY8;
        }
        else
        {
            return -1;
        }
    }
    return -1;
}

void CItem::ModifyPoints(bool bAdd)
{
    int accessoryGrade;

    if (false == IsAccessoryForSocket())
    {
        if (m_pProto->bType == ITEM_WEAPON || m_pProto->bType == ITEM_ARMOR)
        {
            for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
            {
                DWORD dwVnum;

                if ((dwVnum = GetSocket(i)) <= 2)
                    continue;

                TItemTable * p = ITEM_MANAGER::instance().GetTable(dwVnum);

                if (!p)
                {
                    sys_err("cannot find table by vnum %u", dwVnum);
                    continue;
                }

                if (ITEM_METIN == p->bType)
                {
                    for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
                    {
                        if (p->aApplies[i].bType == APPLY_NONE)
                            continue;

                        if (p->aApplies[i].bType == APPLY_SKILL)
                            m_pOwner->ApplyPoint(p->aApplies[i].bType, bAdd ? p->aApplies[i].lValue : p->aApplies[i].lValue ^ 0x00800000);
                        else
                            m_pOwner->ApplyPoint(p->aApplies[i].bType, bAdd ? p->aApplies[i].lValue : -p->aApplies[i].lValue);
                    }
                }
            }
        }

        accessoryGrade = 0;
    }
    else
    {
        accessoryGrade = MIN(GetAccessorySocketGrade(), ITEM_ACCESSORY_SOCKET_MAX_NUM);
    }

    for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
    {
#ifdef __SASH_SYSTEM__
        if ((m_pProto->aApplies[i].bType == APPLY_NONE) && (GetType() != ITEM_COSTUME) && (GetSubType() != COSTUME_SASH))
#else
        if (m_pProto->aApplies[i].bType == APPLY_NONE)
#endif
            continue;

        long value = m_pProto->aApplies[i].lValue;

#ifdef __SASH_SYSTEM__
        BYTE bType = m_pProto->aApplies[i].bType;
        if ((GetType() == ITEM_COSTUME) && (GetSubType() == COSTUME_SASH))
        {
            TItemTable * pkItemAbsorbed = ITEM_MANAGER::instance().GetTable(GetSocket(SASH_ABSORBED_SOCKET));
            if (pkItemAbsorbed)
            {
                if (pkItemAbsorbed->aApplies[i].bType == APPLY_NONE)
                    continue;
                
                bType = pkItemAbsorbed->aApplies[i].bType;
                value = pkItemAbsorbed->aApplies[i].lValue;
                if (value < 0)
                    continue;
                
                double dValue = value * GetSocket(SASH_ABSORPTION_SOCKET);
                dValue = (double)dValue / 100;
                dValue = (double)dValue + .5;
                value = (long) dValue;
                if ((pkItemAbsorbed->aApplies[i].lValue > 0) && (value <= 0))
                    value += 1;
            }
            else
                continue;
        }
#endif
        
        if (m_pProto->aApplies[i].bType == APPLY_SKILL)
        {
            m_pOwner->ApplyPoint(m_pProto->aApplies[i].bType, bAdd ? value : value ^ 0x00800000);
        }
        else
        {
            if (0 != accessoryGrade)
                value += MAX(accessoryGrade, value * aiAccessorySocketEffectivePct[accessoryGrade] / 100);

            m_pOwner->ApplyPoint(m_pProto->aApplies[i].bType, bAdd ? value : -value);
        }
    }
    if (true == CItemVnumHelper::IsRamadanMoonRing(GetVnum()) || true == CItemVnumHelper::IsHalloweenCandy(GetVnum())
        || true == CItemVnumHelper::IsHappinessRing(GetVnum()) || true == CItemVnumHelper::IsLovePendant(GetVnum()))
    {
        // Do not anything.
    }
    else
    {
        for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
        {
            if (GetAttributeType(i))
            {
                const TPlayerItemAttribute& ia = GetAttribute(i);

                if (ia.bType == APPLY_SKILL)
                    m_pOwner->ApplyPoint(ia.bType, bAdd ? ia.sValue : ia.sValue ^ 0x00800000);
                else
                    m_pOwner->ApplyPoint(ia.bType, bAdd ? ia.sValue : -ia.sValue);
            }
        }
    }

    switch (m_pProto->bType)
    {
        case ITEM_PICK:
        case ITEM_ROD:
            {
                if (bAdd)
                {
                    if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
                        m_pOwner->SetPart(PART_WEAPON, GetVnum());
                }
                else
                {
                    if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
#ifdef __WEAPON_COSTUME_SYSTEM__
                        m_pOwner->SetPart(PART_WEAPON, 0);
#else
                        m_pOwner->SetPart(PART_WEAPON, m_pOwner->GetOriginalPart(PART_WEAPON));
#endif
                }
            }
            break;

        case ITEM_WEAPON:
            {
#ifdef __WEAPON_COSTUME_SYSTEM__
                if (m_pOwner->GetWear(WEAR_COSTUME_WEAPON) != 0)
                    break;
#endif
                if (bAdd)
                {
                    if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
                        m_pOwner->SetPart(PART_WEAPON, GetVnum());
                }
                else
                {
                    if (m_wCell == INVENTORY_MAX_NUM + WEAR_WEAPON)
#ifdef __WEAPON_COSTUME_SYSTEM__
                        m_pOwner->SetPart(PART_WEAPON, 0);
#else
                        m_pOwner->SetPart(PART_WEAPON, m_pOwner->GetOriginalPart(PART_WEAPON));
#endif
                }
            }
            break;

        case ITEM_ARMOR:
            {
                if (0 != m_pOwner->GetWear(WEAR_COSTUME_BODY))
                    break;

                if (GetSubType() == ARMOR_BODY || GetSubType() == ARMOR_HEAD || GetSubType() == ARMOR_FOOTS || GetSubType() == ARMOR_SHIELD)
                {
                    if (bAdd)
                    {
                        if (GetProto()->bSubType == ARMOR_BODY)
                            m_pOwner->SetPart(PART_MAIN, GetVnum());
                    }
                    else
                    {
                        if (GetProto()->bSubType == ARMOR_BODY)
                            m_pOwner->SetPart(PART_MAIN, m_pOwner->GetOriginalPart(PART_MAIN));
                    }
                }
            }
            break;

        case ITEM_COSTUME:
            {
                DWORD toSetValue = this->GetVnum();
                EParts toSetPart = PART_MAX_NUM;

                if (GetSubType() == COSTUME_BODY)
                {
                    toSetPart = PART_MAIN;

                    if (false == bAdd)
                    {
                        const CItem* pArmor = m_pOwner->GetWear(WEAR_BODY);
                        toSetValue = (NULL != pArmor) ? pArmor->GetVnum() : m_pOwner->GetOriginalPart(PART_MAIN);                        
                    }
                    
                }
#ifdef __WEAPON_COSTUME_SYSTEM__
                else if (GetSubType() == COSTUME_WEAPON)
                {
                    toSetPart = PART_WEAPON;
                    if (!bAdd)
                    {
                        const CItem* pWeapon = m_pOwner->GetWear(WEAR_WEAPON);
                        toSetValue = (NULL != pWeapon) ? pWeapon->GetVnum() : m_pOwner->GetPart(PART_WEAPON);                        
                    }
                }
#endif

                else if (GetSubType() == COSTUME_HAIR)
                {
                    toSetPart = PART_HAIR;

                    toSetValue = (true == bAdd) ? this->GetValue(3) : 0;
                }

#ifdef __SASH_SYSTEM__
                else if (GetSubType() == COSTUME_SASH)
                {
                    toSetValue -= 85000;
                    if (GetSocket(SASH_ABSORPTION_SOCKET) >= SASH_EFFECT_FROM_ABS)
                        toSetValue += 1000;
                    
                    toSetValue = (bAdd == true) ? toSetValue : 0;
                    toSetPart = PART_SASH;
                }
#endif
                
                if (PART_MAX_NUM != toSetPart)
                {
                    m_pOwner->SetPart((BYTE)toSetPart, toSetValue);
                    m_pOwner->UpdatePacket();
                }
            }
            break;
        case ITEM_UNIQUE:
            {
                if (0 != GetSIGVnum())
                {
                    const CSpecialItemGroup* pItemGroup = ITEM_MANAGER::instance().GetSpecialItemGroup(GetSIGVnum());
                    if (NULL == pItemGroup)
                        break;
                    DWORD dwAttrVnum = pItemGroup->GetAttrVnum(GetVnum());
                    const CSpecialAttrGroup* pAttrGroup = ITEM_MANAGER::instance().GetSpecialAttrGroup(dwAttrVnum);
                    if (NULL == pAttrGroup)
                        break;
                    for (itertype (pAttrGroup->m_vecAttrs) it = pAttrGroup->m_vecAttrs.begin(); it != pAttrGroup->m_vecAttrs.end(); it++)
                    {
                        m_pOwner->ApplyPoint(it->apply_type, bAdd ? it->apply_value : -it->apply_value);
                    }
                }
            }
            break;
    }
}

bool CItem::IsEquipable() const
{
    switch (this->GetType())
    {
    case ITEM_COSTUME:
    case ITEM_ARMOR:
    case ITEM_WEAPON:
    case ITEM_ROD:
    case ITEM_PICK:
    case ITEM_UNIQUE:
    case ITEM_DS:
    case ITEM_SPECIAL_DS:
    case ITEM_RING:
    case ITEM_BELT:
        return true;
    }

    return false;
}

// return false on error state
bool CItem::EquipTo(LPCHARACTER ch, BYTE bWearCell)
{
    if (!ch)
    {
        sys_err("EquipTo: nil character");
        return false;
    }

    // 용혼석 슬롯 index는 WEAR_MAX_NUM 보다 큼.
    if (IsDragonSoul())
    {
        if (bWearCell < WEAR_MAX_NUM || bWearCell >= WEAR_MAX_NUM + DRAGON_SOUL_DECK_MAX_NUM * DS_SLOT_MAX)
        {
            sys_err("EquipTo: invalid dragon soul cell (this: #%d %s wearflag: %d cell: %d)", GetOriginalVnum(), GetName(), GetSubType(), bWearCell - WEAR_MAX_NUM);
            return false;
        }
    }
    else
    {
        if (bWearCell >= WEAR_MAX_NUM)
        {
            sys_err("EquipTo: invalid wear cell (this: #%d %s wearflag: %d cell: %d)", GetOriginalVnum(), GetName(), GetWearFlag(), bWearCell);
            return false;
        }
    }

    if (ch->GetWear(bWearCell))
    {
        sys_err("EquipTo: item already exist (this: #%d %s cell: %d %s)", GetOriginalVnum(), GetName(), bWearCell, ch->GetWear(bWearCell)->GetName());
        return false;
    }

    if (GetOwner())
        RemoveFromCharacter();

    ch->SetWear(bWearCell, this); // 여기서 패킷 나감

    m_pOwner = ch;
    m_bEquipped = true;
    m_wCell    = INVENTORY_MAX_NUM + bWearCell;

    DWORD dwImmuneFlag = 0;

    for (int i = 0 ; i < WEAR_MAX_NUM; i++) 
    {
        LPITEM pItem = m_pOwner->GetWear(i);
        if (pItem)
        {
            SET_BIT(dwImmuneFlag, m_pOwner->GetWear(i)->GetImmuneFlag());
        }
    }

    if (IsDragonSoul())
    {
        DSManager::instance().ActivateDragonSoul(this);
    }
    else
    {
        ModifyPoints(true);    
        StartUniqueExpireEvent();
        if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
            StartTimerBasedOnWearExpireEvent();

        // ACCESSORY_REFINE
        StartAccessorySocketExpireEvent();
        // END_OF_ACCESSORY_REFINE
    }

    ch->BuffOnAttr_AddBuffsFromItem(this);

    m_pOwner->ComputeBattlePoints();

    m_pOwner->UpdatePacket();

    Save();

    return (true);
}

bool CItem::Unequip()
{
    if (!m_pOwner || GetCell() < INVENTORY_MAX_NUM)
    {
        // ITEM_OWNER_INVALID_PTR_BUG
        sys_err("%s %u m_pOwner %p, GetCell %d", 
                GetName(), GetID(), get_pointer(m_pOwner), GetCell());
        // END_OF_ITEM_OWNER_INVALID_PTR_BUG
        return false;
    }

    if (this != m_pOwner->GetWear(GetCell() - INVENTORY_MAX_NUM))
    {
        sys_err("m_pOwner->GetWear() != this");
        return false;
    }

    //신규 말 아이템 제거시 처리
    if (IsRideItem())
        ClearMountAttributeAndAffect();

    if (IsDragonSoul())
    {
        DSManager::instance().DeactivateDragonSoul(this);
    }
    else
    {
        ModifyPoints(false);
    }

    StopUniqueExpireEvent();

    if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
        StopTimerBasedOnWearExpireEvent();

    // ACCESSORY_REFINE
    StopAccessorySocketExpireEvent();
    // END_OF_ACCESSORY_REFINE


    m_pOwner->BuffOnAttr_RemoveBuffsFromItem(this);

    m_pOwner->SetWear(GetCell() - INVENTORY_MAX_NUM, NULL);

    DWORD dwImmuneFlag = 0;
     
    for (int i = 0 ; i < WEAR_MAX_NUM; i++)
    {
        LPITEM pItem = m_pOwner->GetWear(i);
        if (pItem)
        {
            SET_BIT(dwImmuneFlag, m_pOwner->GetWear(i)->GetImmuneFlag());
        }
    }

    m_pOwner->ComputeBattlePoints();

    m_pOwner->UpdatePacket();

    m_pOwner = NULL;
    m_wCell = 0;
    m_bEquipped    = false;

    return true;
}

long CItem::GetValue(DWORD idx)
{
    assert(idx < ITEM_VALUES_MAX_NUM);
    return GetProto()->alValues[idx];
}

void CItem::SetExchanging(bool bOn)
{
    m_bExchanging = bOn;
}

void CItem::Save()
{
    if (m_bSkipSave)
        return;

    ITEM_MANAGER::instance().DelayedSave(this);
}

bool CItem::CreateSocket(BYTE bSlot, BYTE bGold)
{
    assert(bSlot < ITEM_SOCKET_MAX_NUM);

    if (m_alSockets[bSlot] != 0)
    {
        sys_err("Item::CreateSocket : socket already exist %s %d", GetName(), bSlot);
        return false;
    }

    if (bGold)
        m_alSockets[bSlot] = 2;
    else
        m_alSockets[bSlot] = 1;

    UpdatePacket();

    Save();
    return true;
}

void CItem::SetSockets(const long * c_al)
{
    thecore_memcpy(m_alSockets, c_al, sizeof(m_alSockets));
    Save();
}

void CItem::SetSocket(int i, long v, bool bLog)
{
    assert(i < ITEM_SOCKET_MAX_NUM);
    m_alSockets[i] = v;
    UpdatePacket();
    Save();
    if (bLog)
        LogManager::instance().ItemLog(i, v, 0, GetID(), "SET_SOCKET", "", "", GetOriginalVnum());
}

int CItem::GetGold()
{
    if (IS_SET(GetFlag(), ITEM_FLAG_COUNT_PER_1GOLD))
    {
        if (GetProto()->dwGold == 0)
            return GetCount();
        else
            return GetCount() / GetProto()->dwGold;
    }
    else
        return GetProto()->dwGold;
}

int CItem::GetShopBuyPrice()
{
    return GetProto()->dwShopBuyPrice;
}

bool CItem::IsOwnership(LPCHARACTER ch)
{
    if (!m_pkOwnershipEvent)
        return true;

    return m_dwOwnershipPID == ch->GetPlayerID() ? true : false;
}

EVENTFUNC(ownership_event)
{
    item_event_info* info = dynamic_cast<item_event_info*>( event->info );

    if ( info == NULL )
    {
        sys_err( "ownership_event> <Factor> Null pointer" );
        return 0;
    }

    LPITEM pkItem = info->item;

    pkItem->SetOwnershipEvent(NULL);

    TPacketGCItemOwnership p;

    p.bHeader    = HEADER_GC_ITEM_OWNERSHIP;
    p.dwVID    = pkItem->GetVID();
    p.szName[0]    = '\0';

    pkItem->PacketAround(&p, sizeof(p));
    return 0;
}

void CItem::SetOwnershipEvent(LPEVENT pkEvent)
{
    m_pkOwnershipEvent = pkEvent;
}

void CItem::SetOwnership(LPCHARACTER ch, int iSec)
{
    if (!ch)
    {
        if (m_pkOwnershipEvent)
        {
            event_cancel(&m_pkOwnershipEvent);
            m_dwOwnershipPID = 0;

            TPacketGCItemOwnership p;

            p.bHeader    = HEADER_GC_ITEM_OWNERSHIP;
            p.dwVID    = m_dwVID;
            p.szName[0]    = '\0';

            PacketAround(&p, sizeof(p));
        }
        return;
    }

    if (m_pkOwnershipEvent)
        return;

    if (true == LC_IsEurope())
    {
        if (iSec <= 10)
            iSec = 30;
    }

    m_dwOwnershipPID = ch->GetPlayerID();

    item_event_info* info = AllocEventInfo<item_event_info>();
    strlcpy(info->szOwnerName, ch->GetName(), sizeof(info->szOwnerName));
    info->item = this;

    SetOwnershipEvent(event_create(ownership_event, info, PASSES_PER_SEC(iSec)));

    TPacketGCItemOwnership p;

    p.bHeader = HEADER_GC_ITEM_OWNERSHIP;
    p.dwVID = m_dwVID;
    strlcpy(p.szName, ch->GetName(), sizeof(p.szName));

    PacketAround(&p, sizeof(p));
}

int CItem::GetSocketCount()
{
    for (int i = 0; i < ITEM_SOCKET_MAX_NUM; i++)
    {
        if (GetSocket(i) == 0)
            return i;
    }
    return ITEM_SOCKET_MAX_NUM;
}

bool CItem::AddSocket()
{
    int count = GetSocketCount();
    if (count == ITEM_SOCKET_MAX_NUM)
        return false;
    m_alSockets[count] = 1;
    return true;
}

void CItem::AlterToSocketItem(int iSocketCount)
{
    if (iSocketCount >= ITEM_SOCKET_MAX_NUM)
    {
        sys_log(0, "Invalid Socket Count %d, set to maximum", ITEM_SOCKET_MAX_NUM);
        iSocketCount = ITEM_SOCKET_MAX_NUM;
    }

    for (int i = 0; i < iSocketCount; ++i)
        SetSocket(i, 1);
}

void CItem::AlterToMagicItem()
{
    int idx = GetAttributeSetIndex();

    if (idx < 0)
        return;

    //      Appeariance Second Third
    // Weapon 50        20     5
    // Armor  30        10     2
    // Acc    20        10     1

    int iSecondPct;
    int iThirdPct;

    if (g_iUseLocale)
    {
        switch (GetType())
        {
            case ITEM_WEAPON:
                iSecondPct = 20;
                iThirdPct = 5;
                break;

            case ITEM_ARMOR:
            case ITEM_COSTUME:
                if (GetSubType() == ARMOR_BODY)
                {
                    iSecondPct = 10;
                    iThirdPct = 2;
                }
                else
                {
                    iSecondPct = 10;
                    iThirdPct = 1;
                }
                break;

            default:
                return;
        }
    }
    else
    {
        switch (GetType())
        {
            case ITEM_WEAPON:
                iSecondPct = 30;
                iThirdPct = 15;
                break;

            case ITEM_ARMOR:
            case ITEM_COSTUME:
                if (GetSubType() == ARMOR_BODY)
                {
                    iSecondPct = 20;
                    iThirdPct = 10;
                }
                else
                {
                    iSecondPct = 10;
                    iThirdPct = 5;
                }
                break;

            default:
                return;
        }
    }

    // 100% 확률로 좋은 속성 하나
    PutAttribute(aiItemMagicAttributePercentHigh);

    if (number(1, 100) <= iSecondPct)
        PutAttribute(aiItemMagicAttributePercentLow);

    if (number(1, 100) <= iThirdPct)
        PutAttribute(aiItemMagicAttributePercentLow);
}

DWORD CItem::GetRefineFromVnum()
{
    return ITEM_MANAGER::instance().GetRefineFromVnum(GetVnum());
}

int CItem::GetRefineLevel()
{
    const char* name = GetBaseName();
    char* p = const_cast<char*>(strrchr(name, '+'));

    if (!p)
        return 0;

    int    rtn = 0;
    str_to_number(rtn, p+1);

    const char* locale_name = GetName();
    p = const_cast<char*>(strrchr(locale_name, '+'));

    if (p)
    {
        int    locale_rtn = 0;
        str_to_number(locale_rtn, p+1);
        if (locale_rtn != rtn)
        {
            sys_err("refine_level_based_on_NAME(%d) is not equal to refine_level_based_on_LOCALE_NAME(%d).", rtn, locale_rtn);
        }
    }

    return rtn;
}

bool CItem::IsPolymorphItem()
{
    return GetType() == ITEM_POLYMORPH;
}

EVENTFUNC(unique_expire_event)
{
    item_event_info* info = dynamic_cast<item_event_info*>( event->info );

    if ( info == NULL )
    {
        sys_err( "unique_expire_event> <Factor> Null pointer" );
        return 0;
    }

    LPITEM pkItem = info->item;

    if (pkItem->GetValue(2) == 0)
    {
        if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) <= 1)
        {
            sys_log(0, "UNIQUE_ITEM: expire %s %u", pkItem->GetName(), pkItem->GetID());
            pkItem->SetUniqueExpireEvent(NULL);
            ITEM_MANAGER::instance().RemoveItem(pkItem, "UNIQUE_EXPIRE");
            return 0;
        }
        else
        {
            pkItem->SetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME, pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - 1);
            return PASSES_PER_SEC(60);
        }
    }
    else
    {
        time_t cur = get_global_time();
        
        if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) <= cur)
        {
            pkItem->SetUniqueExpireEvent(NULL);
            ITEM_MANAGER::instance().RemoveItem(pkItem, "UNIQUE_EXPIRE");
            return 0;
        }
        else
        {
            // 게임 내에 시간제 아이템들이 빠릿빠릿하게 사라지지 않는 버그가 있어
            // 수정
            // by rtsummit
            if (pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - cur < 600)
                return PASSES_PER_SEC(pkItem->GetSocket(ITEM_SOCKET_UNIQUE_REMAIN_TIME) - cur);
            else
                return PASSES_PER_SEC(600);
        }
    }
}

// 시간 후불제
// timer를 시작할 때에 시간 차감하는 것이 아니라, 
// timer가 발화할 때에 timer가 동작한 시간 만큼 시간 차감을 한다.
EVENTFUNC(timer_based_on_wear_expire_event)
{
    item_event_info* info = dynamic_cast<item_event_info*>( event->info );

    if ( info == NULL )
    {
        sys_err( "expire_event <Factor> Null pointer" );
        return 0;
    }

    LPITEM pkItem = info->item;
    int remain_time = pkItem->GetSocket(ITEM_SOCKET_REMAIN_SEC) - processing_time/passes_per_sec;
    if (remain_time <= 0)
    {
        sys_log(0, "ITEM EXPIRED : expired %s %u", pkItem->GetName(), pkItem->GetID());
        pkItem->SetTimerBasedOnWearExpireEvent(NULL);
        pkItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, 0);
    
        // 일단 timer based on wear 용혼석은 시간 다 되었다고 없애지 않는다.
        if (pkItem->IsDragonSoul())
        {
            DSManager::instance().DeactivateDragonSoul(pkItem);
        }
        else
        {
            ITEM_MANAGER::instance().RemoveItem(pkItem, "TIMER_BASED_ON_WEAR_EXPIRE");
        }
        return 0;
    }
    pkItem->SetSocket(ITEM_SOCKET_REMAIN_SEC, remain_time);
    return PASSES_PER_SEC (MIN (60, remain_time));
}

void CItem::SetUniqueExpireEvent(LPEVENT pkEvent)
{
    m_pkUniqueExpireEvent = pkEvent;
}

void CItem::SetTimerBasedOnWearExpireEvent(LPEVENT pkEvent)
{
    m_pkTimerBasedOnWearExpireEvent = pkEvent;
}

EVENTFUNC(real_time_expire_event)
{
    const item_vid_event_info* info = reinterpret_cast<const item_vid_event_info*>(event->info);

    if (NULL == info)
        return 0;

    const LPITEM item = ITEM_MANAGER::instance().FindByVID( info->item_vid );

    if (NULL == item)
        return 0;

    const time_t current = get_global_time();

    if (current > item->GetSocket(0))
    {
        switch (item->GetVnum())
        {
            if(item->IsNewMountItem())
            {
                if (item->GetSocket(2) != 0)
                    item->ClearMountAttributeAndAffect();
            }
            break;
        }

        ITEM_MANAGER::instance().RemoveItem(item, "REAL_TIME_EXPIRE");

        return 0;
    }

    return PASSES_PER_SEC(1);
}

void CItem::StartRealTimeExpireEvent()
{
    if (m_pkRealTimeExpireEvent)
        return;
    for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
    {
        if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType || LIMIT_REAL_TIME_START_FIRST_USE == GetProto()->aLimits[i].bType)
        {
            item_vid_event_info* info = AllocEventInfo<item_vid_event_info>();
            info->item_vid = GetVID();

            m_pkRealTimeExpireEvent = event_create( real_time_expire_event, info, PASSES_PER_SEC(1));

            sys_log(0, "REAL_TIME_EXPIRE: StartRealTimeExpireEvent");

            return;
        }
    }
}

bool CItem::IsRealTimeItem()
{
    if(!GetProto())
        return false;
    for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
    {
        if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType)
            return true;
    }
    return false;
}

void CItem::StartUniqueExpireEvent()
{
    if (GetType() != ITEM_UNIQUE)
        return;

    if (m_pkUniqueExpireEvent)
        return;

    //기간제 아이템일 경우 시간제 아이템은 동작하지 않는다
    if (IsRealTimeItem())
        return;

    // HARD CODING
    if (GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
        m_pOwner->ShowAlignment(false);

    int iSec = GetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME);

    if (iSec == 0)
        iSec = 60;
    else
        iSec = MIN(iSec, 60);

    SetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME, 0);

    item_event_info* info = AllocEventInfo<item_event_info>();
    info->item = this;

    SetUniqueExpireEvent(event_create(unique_expire_event, info, PASSES_PER_SEC(iSec)));
}

// 시간 후불제
// timer_based_on_wear_expire_event 설명 참조
void CItem::StartTimerBasedOnWearExpireEvent()
{
    if (m_pkTimerBasedOnWearExpireEvent)
        return;

    //기간제 아이템일 경우 시간제 아이템은 동작하지 않는다
    if (IsRealTimeItem())
        return;

    if (-1 == GetProto()->cLimitTimerBasedOnWearIndex)
        return;

    int iSec = GetSocket(0);
    
    // 남은 시간을 분단위로 끊기 위해...
    if (0 != iSec)
    {
        iSec %= 60;
        if (0 == iSec)
            iSec = 60;
    }

    item_event_info* info = AllocEventInfo<item_event_info>();
    info->item = this;

    SetTimerBasedOnWearExpireEvent(event_create(timer_based_on_wear_expire_event, info, PASSES_PER_SEC(iSec)));
}

void CItem::StopUniqueExpireEvent()
{
    if (!m_pkUniqueExpireEvent)
        return;

    if (GetValue(2) != 0) // 게임시간제 이외의 아이템은 UniqueExpireEvent를 중단할 수 없다.
        return;

    // HARD CODING
    if (GetVnum() == UNIQUE_ITEM_HIDE_ALIGNMENT_TITLE)
        m_pOwner->ShowAlignment(true);

    SetSocket(ITEM_SOCKET_UNIQUE_SAVE_TIME, event_time(m_pkUniqueExpireEvent) / passes_per_sec);
    event_cancel(&m_pkUniqueExpireEvent);

    ITEM_MANAGER::instance().SaveSingleItem(this);
}

void CItem::StopTimerBasedOnWearExpireEvent()
{
    if (!m_pkTimerBasedOnWearExpireEvent)
        return;

    int remain_time = GetSocket(ITEM_SOCKET_REMAIN_SEC) - event_processing_time(m_pkTimerBasedOnWearExpireEvent) / passes_per_sec;

    SetSocket(ITEM_SOCKET_REMAIN_SEC, remain_time);
    event_cancel(&m_pkTimerBasedOnWearExpireEvent);

    ITEM_MANAGER::instance().SaveSingleItem(this);
}

void CItem::ApplyAddon(int iAddonType)
{
    CItemAddonManager::instance().ApplyAddonTo(iAddonType, this);
}

int CItem::GetSpecialGroup() const
{ 
    return ITEM_MANAGER::instance().GetSpecialGroupFromItem(GetVnum()); 
}

//
// 악세서리 소켓 처리.
//
bool CItem::IsAccessoryForSocket()
{
    return (m_pProto->bType == ITEM_ARMOR && (m_pProto->bSubType == ARMOR_WRIST || m_pProto->bSubType == ARMOR_NECK || m_pProto->bSubType == ARMOR_EAR)) ||
        (m_pProto->bType == ITEM_BELT);                // 2013년 2월 새로 추가된 '벨트' 아이템의 경우 기획팀에서 악세서리 소켓 시스템을 그대로 이용하자고 함.
}

void CItem::SetAccessorySocketGrade(int iGrade) 
{ 
    SetSocket(0, MINMAX(0, iGrade, GetAccessorySocketMaxGrade())); 

    int iDownTime = aiAccessorySocketDegradeTime[GetAccessorySocketGrade()];

    //if (test_server)
    //    iDownTime /= 60;

    SetAccessorySocketDownGradeTime(iDownTime);
}

void CItem::SetAccessorySocketMaxGrade(int iMaxGrade) 
{ 
    SetSocket(1, MINMAX(0, iMaxGrade, ITEM_ACCESSORY_SOCKET_MAX_NUM)); 
}

void CItem::SetAccessorySocketDownGradeTime(DWORD time) 
{ 
    SetSocket(2, time); 

    if (test_server && GetOwner())
        GetOwner()->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s에서 소켓 빠질때까지 남은 시간 %d"), GetName(), time);
}

EVENTFUNC(accessory_socket_expire_event)
{
    item_vid_event_info* info = dynamic_cast<item_vid_event_info*>( event->info );

    if ( info == NULL )
    {
        sys_err( "accessory_socket_expire_event> <Factor> Null pointer" );
        return 0;
    }

    LPITEM item = ITEM_MANAGER::instance().FindByVID(info->item_vid);

    if (item->GetAccessorySocketDownGradeTime() <= 1)
    {
degrade:
        item->SetAccessorySocketExpireEvent(NULL);
        item->AccessorySocketDegrade();
        return 0;
    }
    else
    {
        int iTime = item->GetAccessorySocketDownGradeTime() - 60;

        if (iTime <= 1)
            goto degrade;

        item->SetAccessorySocketDownGradeTime(iTime);

        if (iTime > 60)
            return PASSES_PER_SEC(60);
        else
            return PASSES_PER_SEC(iTime);
    }
}

void CItem::StartAccessorySocketExpireEvent()
{
    if (!IsAccessoryForSocket())
        return;

    if (m_pkAccessorySocketExpireEvent)
        return;

    if (GetAccessorySocketMaxGrade() == 0)
        return;

    if (GetAccessorySocketGrade() == 0)
        return;

    int iSec = GetAccessorySocketDownGradeTime();
    SetAccessorySocketExpireEvent(NULL);

    if (iSec <= 1)
        iSec = 5;
    else
        iSec = MIN(iSec, 60);

    item_vid_event_info* info = AllocEventInfo<item_vid_event_info>();
    info->item_vid = GetVID();

    SetAccessorySocketExpireEvent(event_create(accessory_socket_expire_event, info, PASSES_PER_SEC(iSec)));
}

void CItem::StopAccessorySocketExpireEvent()
{
    if (!m_pkAccessorySocketExpireEvent)
        return;

    if (!IsAccessoryForSocket())
        return;

    int new_time = GetAccessorySocketDownGradeTime() - (60 - event_time(m_pkAccessorySocketExpireEvent) / passes_per_sec);

    event_cancel(&m_pkAccessorySocketExpireEvent);

    if (new_time <= 1)
    {
        AccessorySocketDegrade();
    }
    else
    {
        SetAccessorySocketDownGradeTime(new_time);
    }
}
        
bool CItem::IsRideItem()
{
    if (ITEM_UNIQUE == GetType() && UNIQUE_SPECIAL_RIDE == GetSubType())
        return true;
    if (ITEM_UNIQUE == GetType() && UNIQUE_SPECIAL_MOUNT_RIDE == GetSubType())
        return true;
    return false;
}
        
//bool CItem::IsRamadanRing()
//{
//    if (GetVnum() == UNIQUE_ITEM_RAMADAN_RING)
//        return true;
//    return false;
//}

void CItem::ClearMountAttributeAndAffect()
{
    LPCHARACTER ch = GetOwner();

    ch->RemoveAffect(AFFECT_MOUNT);
    ch->RemoveAffect(AFFECT_MOUNT_BONUS);

    ch->MountVnum(0);

    ch->PointChange(POINT_ST, 0);
    ch->PointChange(POINT_DX, 0);
    ch->PointChange(POINT_HT, 0);
    ch->PointChange(POINT_IQ, 0);
}

// fixme
// 이거 지금은 안쓴데... 근데 혹시나 싶어서 남겨둠.
// by rtsummit
bool CItem::IsNewMountItem()
{
    switch(GetVnum())
    {
        case 76000: case 76001: case 76002: case 76003: 
        case 76004: case 76005: case 76006: case 76007:
        case 76008: case 76009: case 76010: case 76011: 
        case 76012: case 76013: case 76014:
            return true;
    }
    return false;
}

void CItem::SetAccessorySocketExpireEvent(LPEVENT pkEvent)
{
    m_pkAccessorySocketExpireEvent = pkEvent;
}

void CItem::AccessorySocketDegrade()
{
    if (GetAccessorySocketGrade() > 0)
    {
        LPCHARACTER ch = GetOwner();

        if (ch)
        {
            ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("%s에 박혀있던 보석이 사라집니다."), GetName());
        }

        ModifyPoints(false);
        SetAccessorySocketGrade(GetAccessorySocketGrade()-1);
        ModifyPoints(true);

        int iDownTime = aiAccessorySocketDegradeTime[GetAccessorySocketGrade()];

        if (test_server)
            iDownTime /= 60;

        SetAccessorySocketDownGradeTime(iDownTime);

        if (iDownTime)
            StartAccessorySocketExpireEvent();
    }
}

// ring에 item을 박을 수 있는지 여부를 체크해서 리턴
static const bool CanPutIntoRing(LPITEM ring, LPITEM item)
{
    const DWORD vnum = item->GetVnum();
    return false;
}

bool CItem::CanPutInto(LPITEM item)
{
    if (item->GetType() == ITEM_BELT)
        return this->GetSubType() == USE_PUT_INTO_BELT_SOCKET;

    else if(item->GetType() == ITEM_RING)
        return CanPutIntoRing(item, this);

    else if (item->GetType() != ITEM_ARMOR)
        return false;

    DWORD vnum = item->GetVnum();

    struct JewelAccessoryInfo
    {
        DWORD jewel;
        DWORD wrist;
        DWORD neck;
        DWORD ear;
    };
    const static JewelAccessoryInfo infos[] = { 
        { 50634, 14420, 16220, 17220 }, 
        { 50635, 14500, 16500, 17500 }, 
        { 50636, 14520, 16520, 17520 }, 
        { 50637, 14540, 16540, 17540 }, 
        { 50638, 14560, 16560, 17560 }, 
    };
    
    DWORD item_type = (item->GetVnum() / 10) * 10;
    for (int i = 0; i < sizeof(infos) / sizeof(infos[0]); i++)
    {
        const JewelAccessoryInfo& info = infos[i];
        switch(item->GetSubType())
        {
        case ARMOR_WRIST:
            if (info.wrist == item_type)
            {
                if (info.jewel == GetVnum())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            break;
        case ARMOR_NECK:
            if (info.neck == item_type)
            {
                if (info.jewel == GetVnum())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            break;
        case ARMOR_EAR:
            if (info.ear == item_type)
            {
                if (info.jewel == GetVnum())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            break;
        }
    }
    if (item->GetSubType() == ARMOR_WRIST)
        vnum -= 14000;
    else if (item->GetSubType() == ARMOR_NECK)
        vnum -= 16000;
    else if (item->GetSubType() == ARMOR_EAR)
        vnum -= 17000;
    else
        return false;

    DWORD type = vnum / 20;

    if (type < 0 || type > 11)
    {
        type = (vnum - 170) / 20;

        if (50623 + type != GetVnum())
            return false;
        else
            return true;
    }
    else if (item->GetVnum() >= 16210 && item->GetVnum() <= 16219)
    {
        if (50625 != GetVnum())
            return false;
        else
            return true;
    }
    else if (item->GetVnum() >= 16230 && item->GetVnum() <= 16239)
    {
        if (50626 != GetVnum())
            return false;
        else
            return true;
    }

    return 50623 + type == GetVnum();
}

bool CItem::CheckItemUseLevel(int nLevel)
{
    for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
    {
        if (this->m_pProto->aLimits[i].bType == LIMIT_LEVEL)
        {
            if (this->m_pProto->aLimits[i].lValue > nLevel) return false;
            else return true;
        }
    }
    return true;
}

long CItem::FindApplyValue(BYTE bApplyType)
{
    if (m_pProto == NULL)
        return 0;

    for (int i = 0; i < ITEM_APPLY_MAX_NUM; ++i)
    {
        if (m_pProto->aApplies[i].bType == bApplyType)
            return m_pProto->aApplies[i].lValue;
    }

    return 0;
}

void CItem::CopySocketTo(LPITEM pItem)
{
    for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
    {
        pItem->m_alSockets[i] = m_alSockets[i];
    }
}

int CItem::GetAccessorySocketGrade()
{
       return MINMAX(0, GetSocket(0), GetAccessorySocketMaxGrade()); 
}

int CItem::GetAccessorySocketMaxGrade()
{
       return MINMAX(0, GetSocket(1), ITEM_ACCESSORY_SOCKET_MAX_NUM);
}

int CItem::GetAccessorySocketDownGradeTime()
{
    return MINMAX(0, GetSocket(2), aiAccessorySocketDegradeTime[GetAccessorySocketGrade()]);
}

void CItem::AttrLog()
{
    const char * pszIP = NULL;

    if (GetOwner() && GetOwner()->GetDesc())
        pszIP = GetOwner()->GetDesc()->GetHostName();

    for (int i = 0; i < ITEM_SOCKET_MAX_NUM; ++i)
    {
        if (m_alSockets[i])
        {
            LogManager::instance().ItemLog(i, m_alSockets[i], 0, GetID(), "INFO_SOCKET", "", pszIP ? pszIP : "", GetOriginalVnum());
        }
    }

    for (int i = 0; i<ITEM_ATTRIBUTE_MAX_NUM; ++i)
    {
        int    type    = m_aAttr[i].bType;
        int value    = m_aAttr[i].sValue;

        if (type)
            LogManager::instance().ItemLog(i, type, value, GetID(), "INFO_ATTR", "", pszIP ? pszIP : "", GetOriginalVnum());
    }
}

int CItem::GetLevelLimit()
{
    for (int i = 0; i < ITEM_LIMIT_MAX_NUM; ++i)
    {
        if (this->m_pProto->aLimits[i].bType == LIMIT_LEVEL)
        {
            return this->m_pProto->aLimits[i].lValue;
        }
    }
    return 0;
}

bool CItem::OnAfterCreatedItem()
{
    // 아이템을 한 번이라도 사용했다면, 그 이후엔 사용 중이지 않아도 시간이 차감되는 방식
    if (-1 != this->GetProto()->cLimitRealTimeFirstUseIndex)
    {
        // Socket1에 아이템의 사용 횟수가 기록되어 있으니, 한 번이라도 사용한 아이템은 타이머를 시작한다.
        if (0 != GetSocket(1))
        {
            StartRealTimeExpireEvent();
        }
    }

    return true;
}

bool CItem::IsDragonSoul()
{
    return GetType() == ITEM_DS;
}

int CItem::GiveMoreTime_Per(float fPercent)
{
    if (IsDragonSoul())
    {
        DWORD duration = DSManager::instance().GetDuration(this);
        int remain_sec = GetSocket(ITEM_SOCKET_REMAIN_SEC);
        int given_time = fPercent * duration / 100;
        if (remain_sec == duration)
            return false;
        if ((given_time + remain_sec) >= duration)
        {
            SetSocket(ITEM_SOCKET_REMAIN_SEC, duration);
            return duration - remain_sec;
        }
        else
        {
            SetSocket(ITEM_SOCKET_REMAIN_SEC, given_time + remain_sec);
            return given_time;
        }
    }
    // 우선 용혼석에 관해서만 하도록 한다.
    else
        return 0;
}

int CItem::GiveMoreTime_Fix(DWORD dwTime)
{
    if (IsDragonSoul())
    {
        DWORD duration = DSManager::instance().GetDuration(this);
        int remain_sec = GetSocket(ITEM_SOCKET_REMAIN_SEC);
        if (remain_sec == duration)
            return false;
        if ((dwTime + remain_sec) >= duration)
        {
            SetSocket(ITEM_SOCKET_REMAIN_SEC, duration);
            return duration - remain_sec; 
        }
        else
        {
            SetSocket(ITEM_SOCKET_REMAIN_SEC, dwTime + remain_sec);
            return dwTime;
        }
    }
    // 우선 용혼석에 관해서만 하도록 한다.
    else
        return 0;
}


int    CItem::GetDuration()
{
    if(!GetProto())
        return -1;

    for (int i=0 ; i < ITEM_LIMIT_MAX_NUM ; i++)
    {
        if (LIMIT_REAL_TIME == GetProto()->aLimits[i].bType)
            return GetProto()->aLimits[i].lValue;
    }
    
    if (-1 != GetProto()->cLimitTimerBasedOnWearIndex)
        return GetProto()->aLimits[GetProto()->cLimitTimerBasedOnWearIndex].lValue;    

    return -1;
}

bool CItem::IsSameSpecialGroup(const LPITEM item) const
{
    // 서로 VNUM이 같다면 같은 그룹인 것으로 간주
    if (this->GetVnum() == item->GetVnum())
        return true;

    if (GetSpecialGroup() && (item->GetSpecialGroup() == GetSpecialGroup()))
        return true;

    return false;
}

DWORD CItem::GetRealImmuneFlag()
{
    DWORD dwImmuneFlag = GetImmuneFlag();

    for (int i = 0; i < ITEM_ATTRIBUTE_MAX_NUM; ++i)
    {
        if (!GetAttribute(i).sValue)
            continue;

        if (GetAttribute(i).bType == APPLY_IMMUNE_STUN)
            SET_BIT(dwImmuneFlag, IMMUNE_STUN);
        else if (GetAttribute(i).bType == APPLY_IMMUNE_SLOW)
            SET_BIT(dwImmuneFlag, IMMUNE_SLOW);
        else if (GetAttribute(i).bType == APPLY_IMMUNE_FALL)
            SET_BIT(dwImmuneFlag, IMMUNE_FALL);
    }

    return dwImmuneFlag;
}
 

Link to comment
https://metin2.dev/topic/34036-add-normal-bonus-for-sash/
Share on other sites

  • Replies 0
  • Created
  • Last Reply

Top Posters In This Topic

Popular Days

Top Posters In This Topic

Don't use any images from : imgur, turkmmop, freakgamers, inforge, hizliresim... Or your content will be deleted without notice...
Use : https://metin2.download/media/add/

Please use https://metin2.download/ when uploading files smaller than 100MB, otherwise the approval will take longer due to manual upload.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.