Yardım Kodlama konusunda yardım lazım

  • Konuyu açan Konuyu açan TheAdmin33
  • Açılış Tarihi Açılış Tarihi
  • Yanıt Yanıt 7
  • Gösterim Gösterim 200
Konu sahibi bu konuda soru soruyor. Sorusu ile ilgili bilgisi olanların yanıtlamasını bekliyor.

TheAdmin33

Ah, bu şarkıların gözü kör olsun
Geliştirici
Yardımsever Üye
Usta Üye
Editör
Mesaj
1.328
Çözümler
75
Beğeni
5.079
Puan
2.859
Ticaret Puanı
0
Sadeleştirilmiş Pet Sistemi V2 ye skill ler eklemek istedim ama eklenen skillerin kaçıncı level olduğu bilgisini tutacak bir yer yoktu bende bu 4 skillin level bilgisini bit halinde paketleyip socket0 da saklamaya karar verdim(bir tek socket0 boş) ve yapay zeka yardımıyla aşağıdaki kodu yazdım. Bu kodu yazarak mantıklı bir şeymi yaptım yoksa saçmaladımmı karar veremedim yorumlarınızı bekliyorum.

kod:
Genişlet Daralt Kopyala
//////////////////////////////////////////////////////////////////////////////Pet Skill////////////////////////////////////////////////
// Bit işlemleri için yardımcı fonksiyonlar
namespace PacketUtils {
    inline uint32_t PackBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
        return (a << 24) | (b << 16) | (c << 8) | d;
    }

    inline uint8_t UnpackByte(uint32_t packed, uint8_t position) {
        return (packed >> (8 * (4 - position))) & 0xFF;
    }
}

void CNewPet::SetSkill(LPITEM skillBook, LPITEM petItem)
{
    if (!skillBook || !petItem)
        return;

    const BYTE newSkillId = skillBook->GetValue(0);

    if (newSkillId == 0)
    {
        sys_err("SetSkill: Geçersiz skill ID: %d", newSkillId);
        return;
    }

    // Skill tablosunu kontrol et
    if (petSkillTable.empty())
    {
        sys_err("SetSkill: Skill tablosu boş!");
        return;
    }

    // Mevcut skill'leri kontrol et ve güncelle
    if (UpdateExistingSkill(petItem, newSkillId))
        return;

    // Yeni skill ekle
    AddNewSkill(petItem, newSkillId);
}

bool CNewPet::UpdateExistingSkill(LPITEM petItem, BYTE skillId)
{
    for (int i = PET_ATTR_SKILL_START; i < PET_ATTR_SKILL_END; ++i)
    {
        if (petItem->GetAttributeType(i) == skillId)
        {
            const auto skillData = FindSkillData(skillId);
            if (!skillData.empty())
            {
                const uint8_t skillLevel = PacketUtils::UnpackByte(petItem->GetSocket(0), i - PET_ATTR_SKILL_START + 1);

                if (skillLevel < skillData.size())
                {
                    petItem->SetForceAttribute(i, skillId, skillData[skillLevel]);
                    //sys_log(0, "Pet skill güncellendi: ID %d, Seviye %d", skillId, skillLevel);
                    return true;
                }
            }
            break;
        }
    }
    return false;
}

bool CNewPet::AddNewSkill(LPITEM petItem, BYTE skillId)
{
    for (int i = PET_ATTR_SKILL_START; i < PET_ATTR_SKILL_END; ++i)
    {
        if (petItem->GetAttributeType(i) == 0)
        {
            const auto skillData = FindSkillData(skillId);

            if (!skillData.empty())
            {
                petItem->SetForceAttribute(i, skillId, skillData[1]); // Varsayılan seviye 1

                // Socket verisini güncelle
                UpdateSkillPacketData(petItem, i - PET_ATTR_SKILL_START + 1, 1);

                sys_log(0, "Yeni pet skill eklendi: ID %d", skillId);
                return true;
            }
            break;
        }
    }

    sys_err("AddNewSkill: Pet için boş skill slotu bulunamadı veya skill verisi yok");
    return false;
}

const std::vector<int>& CNewPet::FindSkillData(BYTE skillId)
{
    static const std::vector<int> emptyVector;

    for (const auto& row : petSkillTable) {
        if (!row.empty() && row[0] == skillId) {
            return row;
        }
    }

    sys_err("FindSkillData: Skill ID %d için veri bulunamadı", skillId);
    return emptyVector;
}

void CNewPet::UpdateSkillPacketData(LPITEM petItem, uint8_t position, uint8_t level)
{
    uint32_t currentPacket = petItem->GetSocket(0);

    // Mevcut byte'ları al
    uint8_t bytes[4];
    for (int i = 1; i <= 4; ++i)
    {
        bytes[i-1] = (i == position) ? level : PacketUtils::UnpackByte(currentPacket, i);
    }

    // Yeni paketi oluştur
    uint32_t newPacket = PacketUtils::PackBytes(bytes[0], bytes[1], bytes[2], bytes[3]);
    petItem->SetSocket(0, newPacket);
}
//////////////////////////////////////////////////////////////////////////////Pet Skill////////////////////////////////////////////////
 
Son düzenleme:
Kodun diğer bağlamlarını bilemem ancak buradaki yöntemi şahsen beğendim.
Birden fazla skill seviyesini tek bir 32bitlik veri olarak işleyip maskelemek çok mantıklı bir yaklaşım.

Ama bu gibi yöntemler daha çok işin içinden çıkılamayacak dediğimiz veya daha spesifik durumlarda tercih ediliyor ve kullanılıyor.
Elbette bu "başka hiçbir şekilde kullanılması doğru değil" anlamına gelmiyor. Fakat çok spesifik olmayan bir senaryo için, çok spesifik bir çözüm yolu izlemenin de kafa karışıklığı, okunabilirlik vb. açısından gerek olmadığını düşünüyorum.

Mesela örnek bir senaryo göstereyim:
int veri = 0; şeklinde bir tanım var diyelim.

1.Butona bastığımda: veri = 1 olacak ve A fonksiyonu çalışacak.
2.Butona bastığımda: veri = 2 olacak ve B fonksiyonunu çalışacak.
Ama ben şunu da istiyorum;
Bu iki butonun birbirini etkilemesini asla istemiyorum. Yani 2.butona bastığımda mutlak olarak veri = 2 olacak ama eğer 1.butonu hala kapatmadıysam onun da 1 olarak çalışmaya devam etmesini istiyorum.
İşte burada veri değişkeninin alacağı değerlere bit maskeleme uygulanabilir ve aynı anda birden fazla değer alarak farklı şekilde çalışmaları sağlanabilir.
Burada aklınıza direkt olarak "ve-veya" operatörleri gelebilir fakat burada bahsettiğim olayın || ya da && operatörleriyle doğrudan bir ilişkisi yoktur.


Sonuç olarak; Kısa vadede; pet skilleri gibi ufak çaplı bir senaryoda kullanışlı bir yöntem. Ve eğer bir sorun yaşamıyorsan bu şekilde de kullanabilirsin. (Saçma değil.)
Fakat bunu uzun vadeli olarak ele alacak olursak enum kullanmanı öneririm. Örneğin ileride skill sayısını veya seviyesini arttırmak ya da özelleştirmek istediğinde enum'a göre biraz daha fazla zorlanabilirsin.
 
Kodun diğer bağlamlarını bilemem ancak buradaki yöntemi şahsen beğendim.
Birden fazla skill seviyesini tek bir 32bitlik veri olarak işleyip maskelemek çok mantıklı bir yaklaşım.

Ama bu gibi yöntemler daha çok işin içinden çıkılamayacak dediğimiz veya daha spesifik durumlarda tercih ediliyor ve kullanılıyor.
Elbette bu "başka hiçbir şekilde kullanılması doğru değil" anlamına gelmiyor. Fakat çok spesifik olmayan bir senaryo için, çok spesifik bir çözüm yolu izlemenin de kafa karışıklığı, okunabilirlik vb. açısından gerek olmadığını düşünüyorum.

Mesela örnek bir senaryo göstereyim:
int veri = 0; şeklinde bir tanım var diyelim.

1.Butona bastığımda: veri = 1 olacak ve A fonksiyonu çalışacak.
2.Butona bastığımda: veri = 2 olacak ve B fonksiyonunu çalışacak.
Ama ben şunu da istiyorum;
Bu iki butonun birbirini etkilemesini asla istemiyorum. Yani 2.butona bastığımda mutlak olarak veri = 2 olacak ama eğer 1.butonu hala kapatmadıysam onun da 1 olarak çalışmaya devam etmesini istiyorum.
İşte burada veri değişkeninin alacağı değerlere bit maskeleme uygulanabilir ve aynı anda birden fazla değer alarak farklı şekilde çalışmaları sağlanabilir.
Burada aklınıza direkt olarak "ve-veya" operatörleri gelebilir fakat burada bahsettiğim olayın || ya da && operatörleriyle doğrudan bir ilişkisi yoktur.


Sonuç olarak; Kısa vadede; pet skilleri gibi ufak çaplı bir senaryoda kullanışlı bir yöntem. Ve eğer bir sorun yaşamıyorsan bu şekilde de kullanabilirsin. (Saçma değil.)
Fakat bunu uzun vadeli olarak ele alacak olursak enum kullanmanı öneririm. Örneğin ileride skill sayısını veya seviyesini arttırmak ya da özelleştirmek istediğinde enum'a göre biraz daha fazla zorlanabilirsin.
Yorum için teşekkürler. Zaten enum içerisindeler ama kodu düzeltmeyi unutmuşuyum, düzelttim.

C++:
Genişlet Daralt Kopyala
        enum ENewPetItemInfo
        {
            NEW_PET_MAX_LEVEL = 120,
            PET_ATTR_SKILL_MAX_LEVEL = 30,
            PET_ATTR_SKILL_START = 3,
            PET_ATTR_SKILL_END = 7,
        };

C++:
Genişlet Daralt Kopyala
    for (int i = ENewPetItemInfo::PET_ATTR_SKILL_START; i < ENewPetItemInfo::PET_ATTR_SKILL_END; ++i)
 
Yorum için teşekkürler. Zaten enum içerisindeler ama kodu düzeltmeyi unutmuşuyum, düzelttim.

C++:
Genişlet Daralt Kopyala
        enum ENewPetItemInfo
        {
            NEW_PET_MAX_LEVEL = 120,
            PET_ATTR_SKILL_MAX_LEVEL = 30,
            PET_ATTR_SKILL_START = 3,
            PET_ATTR_SKILL_END = 7,
        };

C++:
Genişlet Daralt Kopyala
    for (int i = ENewPetItemInfo::PET_ATTR_SKILL_START; i < ENewPetItemInfo::PET_ATTR_SKILL_END; ++i)
C++:
Genişlet Daralt Kopyala
namespace PacketUtils {
    inline uint32_t PackBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
        return (a << 24) | (b << 16) | (c << 8) | d;
    }

    inline uint8_t UnpackByte(uint32_t packed, uint8_t position) {
        return (packed >> (8 * (4 - position))) & 0xFF;
    }
}

Ben bu kısımda gerçekleştirilen bit maskeleme ve burayla bağlantılı olan işlemlere değinmiştim aslında. enum kullandığında izlediğin yola bağlı olarak skill seviyelerini tutman şart olmayabilir.

Mesela:

C++:
Genişlet Daralt Kopyala
enum SkillLevel
{
    LEVEL_0,
    LEVEL_1,
    LEVEL_2,
    LEVEL_3,
    LEVEL_4,
    LEVEL_MAX
   
    /*varsayılan olarak elemanlar 0'dan başlar ve +1 olarak artar.
    Ancak kendin değer vermek istersen verebilirsin:
   
    LEVEL_0 = 0,
    LEVEL_1 = 1001,
    LEVEL_2 = 1002,
    LEVEL_3 = 1003,
    LEVEL_4 = 1004,
   
    gibi..
   
    */
   
};

C++:
Genişlet Daralt Kopyala
///////////////////////////////////////////////////
void SetPetSkillLevel(LPITEM petItem, int curLevel)
{
    if (!petItem || curLevel < LEVEL_0 || curLevel >= LEVEL_MAX)
    {
        sys_err("Invalid parameters!!");
        return;
    }

    switch (curLevel)
    {
        case LEVEL_0:
            petItem->SetSocket(0, LEVEL_1);
            break;
        case LEVEL_1:
            petItem->SetSocket(0, LEVEL_2);
            break;
        case LEVEL_2:
            petItem->SetSocket(0, LEVEL_3);
            break;
        case LEVEL_3:
            petItem->SetSocket(0, LEVEL_4);
            break;
        case LEVEL_4: // örnek: 4.seviye son ise bir sey yapma.
            //chatpacket ile mesaj gonder...
            break;
    }
}

Bu şekilde bir mantık uyguladığında skillerin mevcut seviyesini bir yerde tutmana gerek kalmaz. Çünkü gelen curLevel verisine göre bir sonraki skill seviyesi iteme uygulanır. Ve bu durumda zaten otomatik olarak gelen curLevel değerine göre skill seviyesini de öğrenmiş oluyorsun.

Örnek çağrı:
C++:
Genişlet Daralt Kopyala
[...]
SetPetSkillLevel(petItem, petItem->GetSocket(0))
[...]


Bu sadece farklı bir bakış açısı için örnekti.
 
C++:
Genişlet Daralt Kopyala
namespace PacketUtils {
    inline uint32_t PackBytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
        return (a << 24) | (b << 16) | (c << 8) | d;
    }

    inline uint8_t UnpackByte(uint32_t packed, uint8_t position) {
        return (packed >> (8 * (4 - position))) & 0xFF;
    }
}

Ben bu kısımda gerçekleştirilen bit maskeleme ve burayla bağlantılı olan işlemlere değinmiştim aslında. enum kullandığında izlediğin yola bağlı olarak skill seviyelerini tutman şart olmayabilir.

Mesela:

C++:
Genişlet Daralt Kopyala
enum SkillLevel
{
    LEVEL_0,
    LEVEL_1,
    LEVEL_2,
    LEVEL_3,
    LEVEL_4,
    LEVEL_MAX
 
    /*varsayılan olarak elemanlar 0'dan başlar ve +1 olarak artar.
    Ancak kendin değer vermek istersen verebilirsin:
 
    LEVEL_0 = 0,
    LEVEL_1 = 1001,
    LEVEL_2 = 1002,
    LEVEL_3 = 1003,
    LEVEL_4 = 1004,
 
    gibi..
 
    */
 
};

C++:
Genişlet Daralt Kopyala
///////////////////////////////////////////////////
void SetPetSkillLevel(LPITEM petItem, int curLevel)
{
    if (!petItem || curLevel < LEVEL_0 || curLevel >= LEVEL_MAX)
    {
        sys_err("Invalid parameters!!");
        return;
    }

    switch (curLevel)
    {
        case LEVEL_0:
            petItem->SetSocket(0, LEVEL_1);
            break;
        case LEVEL_1:
            petItem->SetSocket(0, LEVEL_2);
            break;
        case LEVEL_2:
            petItem->SetSocket(0, LEVEL_3);
            break;
        case LEVEL_3:
            petItem->SetSocket(0, LEVEL_4);
            break;
        case LEVEL_4: // örnek: 4.seviye son ise bir sey yapma.
            //chatpacket ile mesaj gonder...
            break;
    }
}

Bu şekilde bir mantık uyguladığında skillerin mevcut seviyesini bir yerde tutmana gerek kalmaz. Çünkü gelen curLevel verisine göre bir sonraki skill seviyesi iteme uygulanır. Ve bu durumda zaten otomatik olarak gelen curLevel değerine göre skill seviyesini de öğrenmiş oluyorsun.

Örnek çağrı:
C++:
Genişlet Daralt Kopyala
[...]
SetPetSkillLevel(petItem, petItem->GetSocket(0))
[...]


Bu sadece farklı bir bakış açısı için örnekti.
anladığım kadarıyla attr.value den yola çıkarak level tespiti yapmayı öneriyorsun ama şöyle bir sorun var;

C++:
Genişlet Daralt Kopyala
static const DWORD petSkillTable[15][31] =
{
    // Canavarlara karşı güçlü
    { APPLY_ATTBONUS_MONSTER, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12 },
 
1743172557504.webp

1743172627290.webp

1743172722406.webp

Selamlar, MetinTwo 'da bu şekilde yazmıştım siz de bu şekilde kullanabilirsiniz. Bu şekilde @Kaiser 'in söylediği methoda gerek kalmaz istediğin gibi skill ve level verebilirsin. Yalnız dikkat etmen gereken bir nokta var benim methodumda 4, 4 bitlere böldüğüm için max değer olacak toplam 15 farklı skill oluşturabilirsin ve her bir skill için 15 seviye verebilirsin. Skill id yi 1 'den başlatman durumunda 15 skill id' yi 0 dan başlatırsan 16 olur ama level her türlü max 15 çünkü 0 level olduğunda zaten socket de 0 olacak.
 
Son düzenleme:
anladığım kadarıyla attr.value den yola çıkarak level tespiti yapmayı öneriyorsun ama şöyle bir sorun var;

C++:
Genişlet Daralt Kopyala
static const DWORD petSkillTable[15][31] =
{
    // Canavarlara karşı güçlü
    { APPLY_ATTBONUS_MONSTER, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9, 10, 10, 11, 12 },
Hayır attr ile ilgili işlemleri yine istediğin gibi yapabilirsin. Benim söylediğim şey sadece;
"skillerin kaçıncı level olduğu bilgisini tutacak bir yer yoktu bende bu 4 skillin level bilgisini bit halinde paketleyip socket0 da saklamaya karar verdim" kısmı içindi. Aşağıda bahsetmek istediğim şeyi olabildiğince açıklamaya çalıştım;

C++:
Genişlet Daralt Kopyala
enum SkillLevel
{
    LEVEL_0,
    LEVEL_1,
    LEVEL_2,
    LEVEL_3,
    LEVEL_4,
    LEVEL_MAX
};

////////////////////////////////
////////////////////////////////
////////////////////////////////

void CNewPet::SetSkill(LPITEM skillBook, LPITEM petItem)
{
    if (!skillBook || !petItem)
        return;

    const BYTE newSkillId = skillBook->GetValue(0);

    if (newSkillId == 0)
    {
        sys_err("SetSkill: Geçersiz skill ID: %d", newSkillId);
        return;
    }

    if (petSkillTable.empty())
    {
        sys_err("SetSkill: Skill tablosu boş!");
        return;
    }

    if (UpdateExistingSkill(petItem, newSkillId))
        return;

    AddNewSkill(petItem, newSkillId);
}

bool CNewPet::UpdateExistingSkill(LPITEM petItem, BYTE skillId)
{
    for (int i = PET_ATTR_SKILL_START; i < PET_ATTR_SKILL_END; ++i)
    {
        if (petItem->GetAttributeType(i) == skillId)
        {
            const auto skillData = FindSkillData(skillId);
            if (!skillData.empty())
            {
                SkillLevel currentLevel = static_cast<SkillLevel>(petItem->GetSocket(0));
                
                if (currentLevel < LEVEL_MAX - 1) // LEVEL_MAX harici
                {
                    SkillLevel newLevel = static_cast<SkillLevel>(currentLevel + 1);
                    petItem->SetSocket(0, newLevel);
                    petItem->SetForceAttribute(i, skillId, skillData[newLevel]);
                    //sys_log(0, "Pet skill güncellendi: ID %d, Seviye %d", skillId, newLevel);
                    return true;
                }
            }
            break;
        }
    }
    return false;
}

bool CNewPet::AddNewSkill(LPITEM petItem, BYTE skillId)
{
    for (int i = PET_ATTR_SKILL_START; i < PET_ATTR_SKILL_END; ++i)
    {
        if (petItem->GetAttributeType(i) == 0)
        {
            const auto skillData = FindSkillData(skillId);
            if (!skillData.empty())
            {
                petItem->SetForceAttribute(i, skillId, skillData[LEVEL_1]); // baslangic sabit
                petItem->SetSocket(0, LEVEL_1);
                sys_log(0, "Yeni pet skill eklendi: ID %d", skillId);
                return true;
            }
            break;
        }
    }
    sys_err("AddNewSkill: Skill verisi yok!");
    return false;
}

const std::vector<int>& CNewPet::FindSkillData(BYTE skillId)
{
    static const std::vector<int> emptyVector;

    for (const auto& row : petSkillTable) {
        if (!row.empty() && row[0] == skillId) {
            return row;
        }
    }

    sys_err("FindSkillData: Skill ID %d için veri bulunamadi", skillId);
    return emptyVector;
}

İlk mesajımda da belirttiğim gibi, "illa şunu kullanman gerek" gibi bir mesajım yok. Sadece alternatif olarak sunmak istedim.
Her iki yöntem de kullanışlıdır. Bu alternatifi sunmamdaki amaç ise eğer "ben okunabilirliğe ve anlaşılabilirliği tercih ederim" düşünüyorsan diye belirtmek istedim.
Teknik açıdan bakıldığında da zaten iki yöntem arasında da bellek ve performans konusunda pek bir fark yok. Kısacası tercihine göre istediğini kullanabilirsin.

Son olarak; pet sistemiyle aram hiç olmadı bugüne kadar. Eğer yapmaya çalıştığın şey "bir pette birden fazla skill olması" gibi bir şey ise şu an ki halini kullanman senin için daha iyi bir seçenektir.
 
item tablosunda efsunların type ve value bilgileri tutulurken level bilgileri tutulmuyor, keşke bunuda ekleselermiş. Sadece bu konuda değil diğer bir çok konuda yardımcı olurdu ve oyuna bununla alakalı yeni şeyler eklenebilirdi(efsunların sınıflandırılması ve buna göre renklendirilmesi vs.).
 
Geri
Üst