From 5644b9a3e9cb0d6a393de7b17ab75dacc7a5390a Mon Sep 17 00:00:00 2001 From: OllumHD Date: Sat, 14 Dec 2024 06:10:40 +0100 Subject: [PATCH 1/3] proper rarities and souvenir handling ive made it so its possible to add the dreamhack 2013 package without the items you get from it having unusual rarity, means that u can add cases to the unusuals file without them being handled as knives as for the rarities, getting knives works fine with a little testing :P --- csgo_gc/item_schema.cpp | 110 ++++++++++++++++++++++++++---- csgo_gc/item_schema.h | 1 + examples/unusual_loot_lists.txt | 114 ++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 12 deletions(-) diff --git a/csgo_gc/item_schema.cpp b/csgo_gc/item_schema.cpp index dbf1105..da7812c 100644 --- a/csgo_gc/item_schema.cpp +++ b/csgo_gc/item_schema.cpp @@ -68,6 +68,7 @@ ItemInfo::ItemInfo(uint32_t defIndex) , m_rarity{ ItemSchema::RarityCommon } , m_quality{ ItemSchema::QualityUnique } , m_supplyCrateSeries{ 0 } + , m_tournamentEventId{ 0 } { // RecursiveParseItem parses the rest } @@ -555,7 +556,13 @@ bool ItemSchema::SelectItemFromCrate(const CSOEconItem &crate, CSOEconItem &item lootListItems.reserve(32); // overkill bool containsUnusuals = GetLootListItems(lootList, lootListItems); - // stattrak def + if (!lootListItems.size()) + { + assert(false); + return false; + } + + // handle stattrak GenerateStatTrak generateStatTrak = GenerateStatTrak::No; if (lootList.willProduceStatTrak) { @@ -566,19 +573,85 @@ bool ItemSchema::SelectItemFromCrate(const CSOEconItem &crate, CSOEconItem &item generateStatTrak = GenerateStatTrak::Maybe; } - if (lootListItems.size()) + // group items by rarity and map rarities to base weights + std::unordered_map> itemsByRarity; + std::unordered_map rarityWeights; + uint32_t totalWeight = 0; + + // rarity weight map + const std::unordered_map baseWeights = { + {RarityDefault, 15625}, // Consumer (Gray) + {RarityCommon, 3125}, // Industrial (Light Blue) + {RarityUncommon, 625}, // Mil-Spec (Blue) + {RarityRare, 125}, // Restricted (Purple) + {RarityMythical, 25}, // Classified (Pink) + {RarityLegendary, 5} // Covert (Red) + }; + + // group items by rarity and calculate total weight + for (const auto* lootItem : lootListItems) { - size_t index = g_random.RandomIndex(lootListItems.size()); - const LootListItem &lootListItem = *lootListItems[index]; - return EconItemFromLootListItem(lootListItem, item, generateStatTrak); + if (lootItem->quality != QualityUnusual) + { + // if we find an item of this rarity, add its base weight to total + if (itemsByRarity[lootItem->rarity].empty() && baseWeights.count(lootItem->rarity)) + { + rarityWeights[lootItem->rarity] = baseWeights.at(lootItem->rarity); + totalWeight += baseWeights.at(lootItem->rarity); + } + itemsByRarity[lootItem->rarity].push_back(lootItem); + } } - else + + // check for golds + if (!lootList.isUnusual && containsUnusuals) { - assert(false); - return false; + std::vector unusualItems; + if (g_random.Uint32(0, totalWeight + 2) < 2) // 2 weight + { + for (const auto* lootItem : lootListItems) + { + if (lootItem->quality == QualityUnusual) + { + unusualItems.push_back(lootItem); + } + } + + if (!unusualItems.empty()) + { + size_t index = g_random.RandomIndex(unusualItems.size()); + return EconItemFromLootListItem(*unusualItems[index], item, generateStatTrak); + } + } } - return true; + uint32_t roll = g_random.Uint32(0, totalWeight); + uint32_t currentWeight = 0; + + for (const auto& [rarity, items] : itemsByRarity) + { + if (rarityWeights.count(rarity)) + { + currentWeight += rarityWeights[rarity]; + if (roll < currentWeight) + { + // random item from this rarity + size_t index = g_random.RandomIndex(items.size()); + bool result = EconItemFromLootListItem(*items[index], item, generateStatTrak); + + // set quality to 9 if souvenir package + if (result && itemSearch->second.m_tournamentEventId != 0) + { + item.set_quality(QualityTournament); + } + + return result; + } + } + } + + assert(false); + return false; } void ItemSchema::ParseItems(const KeyValue *itemsKey, const KeyValue *prefabsKey) @@ -670,6 +743,12 @@ void ItemSchema::ParseItemRecursive(ItemInfo &info, const KeyValue &itemKey, con { info.m_supplyCrateSeries = supplyCrateSeries->GetNumber("value"); } + + const KeyValue* tournamentEventId = attributes->GetSubkey("tournament event id"); + if (tournamentEventId) + { + info.m_tournamentEventId = tournamentEventId->GetNumber("value"); + } } } @@ -792,18 +871,25 @@ static LootListItemType LootListItemTypeFromName(std::string_view name, std::str return LootListItemPaintable; } -void ItemSchema::ParseLootLists(const KeyValue *lootListsKey, bool unusual) +void ItemSchema::ParseLootLists(const KeyValue *lootListsKey, bool parentIsUnusual) { m_lootLists.reserve(lootListsKey->SubkeyCount()); for (const KeyValue &lootListKey : *lootListsKey) { + std::string_view listName = lootListKey.Name(); + + // check if this list should be treated as unusual + bool isUnusual = parentIsUnusual && + (listName.length() >= 8 && + listName.compare(listName.length() - 8, 8, "_unusual") == 0); + auto emplace = m_lootLists.emplace(std::piecewise_construct, std::forward_as_tuple(lootListKey.Name()), std::forward_as_tuple()); LootList &lootList = emplace.first->second; - lootList.isUnusual = unusual; + lootList.isUnusual = isUnusual; // only set unusual if parent is unusual AND name ends with _unusual for (const KeyValue &entryKey : lootListKey) { @@ -841,7 +927,7 @@ void ItemSchema::ParseLootLists(const KeyValue *lootListsKey, bool unusual) LootListItem item; if (ParseLootListItem(item, entryName)) { - if (unusual) + if (isUnusual) { // override the quality here... item.quality = QualityUnusual; diff --git a/csgo_gc/item_schema.h b/csgo_gc/item_schema.h index 51587b0..6d2a3a8 100644 --- a/csgo_gc/item_schema.h +++ b/csgo_gc/item_schema.h @@ -27,6 +27,7 @@ class ItemInfo uint32_t m_rarity; uint32_t m_quality; uint32_t m_supplyCrateSeries; // cases only + uint32_t m_tournamentEventId; // souvenirs only }; class PaintKitInfo diff --git a/examples/unusual_loot_lists.txt b/examples/unusual_loot_lists.txt index 930b188..5186a4c 100644 --- a/examples/unusual_loot_lists.txt +++ b/examples/unusual_loot_lists.txt @@ -1,3 +1,117 @@ +"crate_dhw13_promo_common" +{ + "[hy_desert]weapon_g3sg1" "1" + "[so_sand]weapon_p250" "1" + "[sp_mesh_sand]weapon_scar20" "1" + "[sp_spray_sand]weapon_p90" "1" + "[sp_tape_short_sand]weapon_mp9" "1" + "[sp_zebracam]weapon_nova" "1" + "[so_pmc]weapon_elite" "1" + "[so_pmc]weapon_scar20" "1" + "[so_moss]weapon_ssg08" "1" + "[sp_mesh_army]weapon_tec9" "1" + "[sp_spray_army]weapon_mp7" "1" + "[hy_forest_boreal]weapon_p250" "1" + "[so_moss]weapon_xm1014" "1" + "[so_stormfront]weapon_aug" "1" + "[sp_spray_desert_sage]weapon_galilar" "1" + "[sp_tape_dots_waves]weapon_sg556" "1" + "[sp_tape_short_jungle]weapon_g3sg1" "1" + "[so_olive]weapon_tec9" "1" + "[so_pmc]weapon_aug" "1" + "[so_space_marine]weapon_famas" "1" + "[so_sand]weapon_nova" "1" + "[sp_tape_short_sand]weapon_bizon" "1" + "[hy_ddpat_urb]weapon_ump45" "1" + "[so_space_marine]weapon_elite" "1" + "[hy_arctic_contrast]weapon_g3sg1" "1" + "[hy_forest_night]weapon_fiveseven" "1" + "[sp_mesh_arctic_contrast]weapon_nova" "1" + "[sp_tape_short_urban]weapon_bizon" "1" + "[sp_tape]weapon_p250" "1" + "[so_pmc]weapon_fiveseven" "1" + "[so_space_marine]weapon_aug" "1" + "[sp_mesh_tan]weapon_g3sg1" "1" + "[sp_dapple]weapon_p90" "1" + "[sp_mesh_slashes]weapon_galilar" "1" +} +"crate_dhw13_promo_uncommon" +{ + "[sp_snake]weapon_sawedoff" "1" + "[sp_mesh_tan]weapon_ak47" "1" + "[sp_tape_orange]weapon_fiveseven" "1" + "[sp_palm]weapon_mac10" "1" + "[hy_varicamo]weapon_tec9" "1" + "[sp_leaves]weapon_usp_silencer" "1" + "[sp_mesh_forest_fire]weapon_aug" "1" + "[sp_tape_orange]weapon_mp9" "1" + "[hy_varicamo]weapon_g3sg1" "1" + "[hy_varicamo]weapon_galilar" "1" + "[sp_mesh_python]weapon_m249" "1" + "[aq_blued]weapon_xm1014" "1" + "[sp_mesh_tan]weapon_awp" "1" + "[hy_mottled_sand]weapon_deagle" "1" + "[hy_reef]weapon_famas" "1" + "[hy_varicamo_night]weapon_bizon" "1" + "[so_red]weapon_nova" "1" + "[hy_gelpen]weapon_ump45" "1" + "[hy_granite]weapon_hkp2000" "1" + "[aq_forced]weapon_elite" "1" + "[hy_forest_boreal]weapon_m4a1_silencer" "1" + "[hy_varicamo_desert]weapon_xm1014" "1" + "[so_red]weapon_mac10" "1" + "[hy_ddpat_urb]weapon_m4a1" "1" + "[am_urban]weapon_mag7" "1" + "[am_urban]weapon_p250" "1" + "[am_carbon_fiber]weapon_scar20" "1" + "[sp_twigs]weapon_p90" "1" + "[so_olive]weapon_glock" "1" + "[sp_tape_orange]weapon_mp7" "1" + "[sp_palm_shadow]weapon_ssg08" "1" + "[hy_varicamo_desert]weapon_negev" "1" + "[sp_mesh_python]weapon_sg556" "1" +} +"crate_dhw13_promo_rare" +{ + "[aq_brass]weapon_bizon" "1" + "[hy_varicamo]weapon_m4a1_silencer" "1" + "[aq_damascus_sg553]weapon_sg556" "1" + "[sp_mesh_hot_and_cold]weapon_famas" "1" + "[am_crystallized_silver]weapon_fiveseven" "1" + "[aa_fade_grassland]weapon_ssg08" "1" + "[an_navy]weapon_sg556" "1" + "[hy_varicamo_night]weapon_usp_silencer" "1" + "[sp_mesh_hot_and_cold]weapon_p90" "1" + "[so_red]weapon_glock" "1" + "[an_navy]weapon_mp7" "1" + "[hy_varicamo_red]weapon_sawedoff" "1" + "[hy_varicamo_urban]weapon_deagle" "1" + "[aa_fade_metallic]weapon_sawedoff" "1" + "[an_red]weapon_mp9" "1" + "[aa_flames]weapon_ump45" "1" + "[aa_fade_metallic]weapon_mac10" "1" +} +"crate_dhw13_promo_mythical" +{ + "[aa_fade_metallic]weapon_hkp2000" "1" + "[so_orange_accents]weapon_m4a1_silencer" "1" + "[am_crystallized_blue]weapon_elite" "1" + "[hy_snakeskin]weapon_awp" "1" + "[am_crystallized]weapon_tec9" "1" + "[so_yellow]weapon_mag7" "1" +} +"crate_dhw13_promo_legendary" +{ + "[aa_fade_metallic_revolver]weapon_revolver" "1" +} +"crate_dhw13_promo" +{ + "crate_dhw13_promo_common" "1" + "crate_dhw13_promo_uncommon" "1" + "crate_dhw13_promo_rare" "1" + "crate_dhw13_promo_mythical" "1" + "crate_dhw13_promo_legendary" "1" +} "unusual_revolving_list" { "[aa_fade]weapon_bayonet" "1" From 0b1302ab9ae0cc4c9d4eaf98a7f11e0517dfba50 Mon Sep 17 00:00:00 2001 From: OllumHD Date: Sat, 14 Dec 2024 14:34:05 +0100 Subject: [PATCH 2/3] did an oopsie, check for if the list name CONTAINS unusual instead i made an oversight, i didnt account for "unusual_loot_lists" for example im sorry :( --- csgo_gc/item_schema.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/csgo_gc/item_schema.cpp b/csgo_gc/item_schema.cpp index da7812c..a689843 100644 --- a/csgo_gc/item_schema.cpp +++ b/csgo_gc/item_schema.cpp @@ -880,9 +880,7 @@ void ItemSchema::ParseLootLists(const KeyValue *lootListsKey, bool parentIsUnusu std::string_view listName = lootListKey.Name(); // check if this list should be treated as unusual - bool isUnusual = parentIsUnusual && - (listName.length() >= 8 && - listName.compare(listName.length() - 8, 8, "_unusual") == 0); + bool isUnusual = parentIsUnusual && (listName.find("unusual") != std::string_view::npos); auto emplace = m_lootLists.emplace(std::piecewise_construct, std::forward_as_tuple(lootListKey.Name()), From 32bbb5b04c799d20839661b3cd858e3026eef472 Mon Sep 17 00:00:00 2001 From: OllumHD Date: Sat, 14 Dec 2024 17:20:49 +0100 Subject: [PATCH 3/3] made checking for souvenir packages more precise previously sticker capsules with a tournament id value (basically major stickers) all got souvenir quality, thats fixed now not sure how else to check for souvenir packages, tried checking the prefab but that didnt result in "weapon_case_souvenirpkg" as expected, it returned "weapon_case_base" likely because thats the parent prefab --- csgo_gc/item_schema.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/csgo_gc/item_schema.cpp b/csgo_gc/item_schema.cpp index a689843..9278852 100644 --- a/csgo_gc/item_schema.cpp +++ b/csgo_gc/item_schema.cpp @@ -603,6 +603,14 @@ bool ItemSchema::SelectItemFromCrate(const CSOEconItem &crate, CSOEconItem &item } } + // check for industrial grade items (for souvenir packages) + bool hasIndustrialItems = false; + if (itemsByRarity.count(RarityCommon) > 0 && !itemsByRarity[RarityCommon].empty()) + { + hasIndustrialItems = true; + Platform::Print("[GC] Found consumer grade items (%zu)\n", itemsByRarity[RarityCommon].size()); + } + // check for golds if (!lootList.isUnusual && containsUnusuals) { @@ -640,8 +648,9 @@ bool ItemSchema::SelectItemFromCrate(const CSOEconItem &crate, CSOEconItem &item bool result = EconItemFromLootListItem(*items[index], item, generateStatTrak); // set quality to 9 if souvenir package - if (result && itemSearch->second.m_tournamentEventId != 0) + if (result && itemSearch->second.m_tournamentEventId != 0 && hasIndustrialItems) { + Platform::Print("[GC] Setting quality to Tournament\n"); item.set_quality(QualityTournament); } @@ -887,7 +896,7 @@ void ItemSchema::ParseLootLists(const KeyValue *lootListsKey, bool parentIsUnusu std::forward_as_tuple()); LootList &lootList = emplace.first->second; - lootList.isUnusual = isUnusual; // only set unusual if parent is unusual AND name ends with _unusual + lootList.isUnusual = isUnusual; // only set unusual if parent is unusual AND name contains "unusual" for (const KeyValue &entryKey : lootListKey) {