Core/Loot: Added type to LootStoreItem to replace reference != 0 checks
Port From (https://github.com/TrinityCore/TrinityCore/commit/7e70ea701e554c9e404683014cd3bc1cc4885eaf)
This commit is contained in:
@@ -1449,4 +1449,10 @@ namespace Framework.Constants
|
|||||||
PriorityQuestItems = 0x20,
|
PriorityQuestItems = 0x20,
|
||||||
ExcludeJunkSell = 0x40,
|
ExcludeJunkSell = 0x40,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum LootStoreItemType
|
||||||
|
{
|
||||||
|
Item = 0,
|
||||||
|
Reference = 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+286
-251
@@ -9,6 +9,7 @@ using Game.Entities;
|
|||||||
using Game.Spells;
|
using Game.Spells;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Game.Loots
|
namespace Game.Loots
|
||||||
{
|
{
|
||||||
@@ -98,22 +99,22 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// Remove real entries and check loot existence
|
// Remove real entries and check loot existence
|
||||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||||
foreach (var creatureTemplate in templates.Values)
|
foreach (var (creatureId, creatureTemplate) in templates)
|
||||||
{
|
{
|
||||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||||
{
|
{
|
||||||
if (creatureDifficulty.LootID != 0)
|
if (creatureDifficulty.LootID != 0)
|
||||||
{
|
{
|
||||||
if (!lootIdSet.Contains(creatureDifficulty.LootID))
|
if (!lootIdSet.Contains(creatureDifficulty.LootID))
|
||||||
Creature.ReportNonExistingId(creatureDifficulty.LootID, creatureTemplate.Entry);
|
Creature.ReportNonExistingId(creatureDifficulty.LootID, creatureId);
|
||||||
else
|
else
|
||||||
lootIdSetUsed.Add(creatureDifficulty.LootID);
|
lootIdSetUsed.Add(creatureDifficulty.LootID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in lootIdSetUsed)
|
foreach (var lootId in lootIdSetUsed)
|
||||||
lootIdSet.Remove(id);
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// 1 means loot for player corpse
|
// 1 means loot for player corpse
|
||||||
lootIdSet.Remove(SharedConst.PlayerCorpseLootEntry);
|
lootIdSet.Remove(SharedConst.PlayerCorpseLootEntry);
|
||||||
@@ -136,7 +137,7 @@ namespace Game.Loots
|
|||||||
List<uint> lootIdSet, lootIdSetUsed = new();
|
List<uint> lootIdSet, lootIdSetUsed = new();
|
||||||
uint count = Disenchant.LoadAndCollectLootIds(out lootIdSet);
|
uint count = Disenchant.LoadAndCollectLootIds(out lootIdSet);
|
||||||
|
|
||||||
foreach (var disenchant in CliDB.ItemDisenchantLootStorage.Values)
|
foreach (var (_, disenchant) in CliDB.ItemDisenchantLootStorage)
|
||||||
{
|
{
|
||||||
uint lootid = disenchant.Id;
|
uint lootid = disenchant.Id;
|
||||||
if (!lootIdSet.Contains(lootid))
|
if (!lootIdSet.Contains(lootid))
|
||||||
@@ -145,8 +146,8 @@ namespace Game.Loots
|
|||||||
lootIdSetUsed.Add(lootid);
|
lootIdSetUsed.Add(lootid);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in lootIdSetUsed)
|
foreach (var lootId in lootIdSetUsed)
|
||||||
lootIdSet.Remove(id);
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Disenchant.ReportUnusedIds(lootIdSet);
|
Disenchant.ReportUnusedIds(lootIdSet);
|
||||||
@@ -215,8 +216,8 @@ namespace Game.Loots
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in lootIdSetUsed)
|
foreach (var lootId in lootIdSetUsed)
|
||||||
lootIdSet.Remove(id);
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Gameobject.ReportUnusedIds(lootIdSet);
|
Gameobject.ReportUnusedIds(lootIdSet);
|
||||||
@@ -238,9 +239,10 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
var its = Global.ObjectMgr.GetItemTemplates();
|
var its = Global.ObjectMgr.GetItemTemplates();
|
||||||
foreach (var pair in its)
|
foreach (var (itemId, itemTemplate) in its)
|
||||||
if (lootIdSet.Contains(pair.Value.GetId()) && pair.Value.HasFlag(ItemFlags.HasLoot))
|
if (itemTemplate.HasFlag(ItemFlags.HasLoot))
|
||||||
lootIdSet.Remove(pair.Value.GetId());
|
if (!lootIdSet.Remove(itemId))
|
||||||
|
Items.ReportNonExistingId(itemId, itemId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Items.ReportUnusedIds(lootIdSet);
|
Items.ReportUnusedIds(lootIdSet);
|
||||||
@@ -262,14 +264,10 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
var its = Global.ObjectMgr.GetItemTemplates();
|
var its = Global.ObjectMgr.GetItemTemplates();
|
||||||
foreach (var pair in its)
|
foreach (var (itemId, itemTemplate) in its)
|
||||||
{
|
if (!itemTemplate.HasFlag(ItemFlags.IsMillable))
|
||||||
if (!pair.Value.HasFlag(ItemFlags.IsMillable))
|
if (!lootIdSet.Remove(itemId))
|
||||||
continue;
|
Milling.ReportNonExistingId(itemId, itemId);
|
||||||
|
|
||||||
if (lootIdSet.Contains(pair.Value.GetId()))
|
|
||||||
lootIdSet.Remove(pair.Value.GetId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Milling.ReportUnusedIds(lootIdSet);
|
Milling.ReportUnusedIds(lootIdSet);
|
||||||
@@ -292,22 +290,22 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// Remove real entries and check loot existence
|
// Remove real entries and check loot existence
|
||||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||||
foreach (var creatureTemplate in templates.Values)
|
foreach (var (creatureId, creatureTemplate) in templates)
|
||||||
{
|
{
|
||||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||||
{
|
{
|
||||||
if (creatureDifficulty.PickPocketLootID != 0)
|
if (creatureDifficulty.PickPocketLootID != 0)
|
||||||
{
|
{
|
||||||
if (!lootIdSet.Contains(creatureDifficulty.PickPocketLootID))
|
if (!lootIdSet.Contains(creatureDifficulty.PickPocketLootID))
|
||||||
Pickpocketing.ReportNonExistingId(creatureDifficulty.PickPocketLootID, creatureTemplate.Entry);
|
Pickpocketing.ReportNonExistingId(creatureDifficulty.PickPocketLootID, creatureId);
|
||||||
else
|
else
|
||||||
lootIdSetUsed.Add(creatureDifficulty.PickPocketLootID);
|
lootIdSetUsed.Add(creatureDifficulty.PickPocketLootID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in lootIdSetUsed)
|
foreach (var lootId in lootIdSetUsed)
|
||||||
lootIdSet.Remove(id);
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Pickpocketing.ReportUnusedIds(lootIdSet);
|
Pickpocketing.ReportUnusedIds(lootIdSet);
|
||||||
@@ -329,14 +327,10 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
var its = Global.ObjectMgr.GetItemTemplates();
|
var its = Global.ObjectMgr.GetItemTemplates();
|
||||||
foreach (var pair in its)
|
foreach (var (itemId, itemTemplate) in its)
|
||||||
{
|
if (itemTemplate.HasFlag(ItemFlags.IsProspectable))
|
||||||
if (!pair.Value.HasFlag(ItemFlags.IsProspectable))
|
if (!lootIdSet.Remove(itemId))
|
||||||
continue;
|
Prospecting.ReportNonExistingId(itemId, itemId);
|
||||||
|
|
||||||
if (lootIdSet.Contains(pair.Value.GetId()))
|
|
||||||
lootIdSet.Remove(pair.Value.GetId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Prospecting.ReportUnusedIds(lootIdSet);
|
Prospecting.ReportUnusedIds(lootIdSet);
|
||||||
@@ -357,9 +351,8 @@ namespace Game.Loots
|
|||||||
uint count = Mail.LoadAndCollectLootIds(out lootIdSet);
|
uint count = Mail.LoadAndCollectLootIds(out lootIdSet);
|
||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
foreach (var mail in CliDB.MailTemplateStorage.Values)
|
foreach (var (_, mailTemplate) in CliDB.MailTemplateStorage)
|
||||||
if (lootIdSet.Contains(mail.Id))
|
lootIdSet.Remove(mailTemplate.Id);
|
||||||
lootIdSet.Remove(mail.Id);
|
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Mail.ReportUnusedIds(lootIdSet);
|
Mail.ReportUnusedIds(lootIdSet);
|
||||||
@@ -382,22 +375,22 @@ namespace Game.Loots
|
|||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
var templates = Global.ObjectMgr.GetCreatureTemplates();
|
||||||
foreach (var creatureTemplate in templates.Values)
|
foreach (var (creatureId, creatureTemplate) in templates)
|
||||||
{
|
{
|
||||||
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
foreach (var (_, creatureDifficulty) in creatureTemplate.difficultyStorage)
|
||||||
{
|
{
|
||||||
if (creatureDifficulty.SkinLootID != 0)
|
if (creatureDifficulty.SkinLootID != 0)
|
||||||
{
|
{
|
||||||
if (!lootIdSet.Contains(creatureDifficulty.SkinLootID))
|
if (!lootIdSet.Contains(creatureDifficulty.SkinLootID))
|
||||||
Skinning.ReportNonExistingId(creatureDifficulty.SkinLootID, creatureTemplate.Entry);
|
Skinning.ReportNonExistingId(creatureDifficulty.SkinLootID, creatureId);
|
||||||
else
|
else
|
||||||
lootIdSetUsed.Add(creatureDifficulty.SkinLootID);
|
lootIdSetUsed.Add(creatureDifficulty.SkinLootID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var id in lootIdSetUsed)
|
foreach (var lootId in lootIdSetUsed)
|
||||||
lootIdSet.Remove(id);
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Skinning.ReportUnusedIds(lootIdSet);
|
Skinning.ReportUnusedIds(lootIdSet);
|
||||||
@@ -416,18 +409,15 @@ namespace Game.Loots
|
|||||||
uint oldMSTime = Time.GetMSTime();
|
uint oldMSTime = Time.GetMSTime();
|
||||||
|
|
||||||
List<uint> lootIdSet;
|
List<uint> lootIdSet;
|
||||||
|
List<uint> lootIdSetUsed = new();
|
||||||
uint count = Spell.LoadAndCollectLootIds(out lootIdSet);
|
uint count = Spell.LoadAndCollectLootIds(out lootIdSet);
|
||||||
|
|
||||||
// remove real entries and check existence loot
|
// remove real entries and check existence loot
|
||||||
foreach (SpellNameRecord spellNameEntry in CliDB.SpellNameStorage.Values)
|
Global.SpellMgr.ForEachSpellInfo(spellInfo =>
|
||||||
{
|
{
|
||||||
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellNameEntry.Id, Difficulty.None);
|
|
||||||
if (spellInfo == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// possible cases
|
// possible cases
|
||||||
if (!spellInfo.IsLootCrafting())
|
if (!spellInfo.IsLootCrafting())
|
||||||
continue;
|
return;
|
||||||
|
|
||||||
if (!lootIdSet.Contains(spellInfo.Id))
|
if (!lootIdSet.Contains(spellInfo.Id))
|
||||||
{
|
{
|
||||||
@@ -439,8 +429,11 @@ namespace Game.Loots
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
lootIdSet.Remove(spellInfo.Id);
|
lootIdSetUsed.Add(spellInfo.Id);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
foreach (uint lootId in lootIdSetUsed)
|
||||||
|
lootIdSet.Remove(lootId);
|
||||||
|
|
||||||
// output error for any still listed (not referenced from appropriate table) ids
|
// output error for any still listed (not referenced from appropriate table) ids
|
||||||
Spell.ReportUnusedIds(lootIdSet);
|
Spell.ReportUnusedIds(lootIdSet);
|
||||||
@@ -483,7 +476,7 @@ namespace Game.Loots
|
|||||||
public class LootStoreItem
|
public class LootStoreItem
|
||||||
{
|
{
|
||||||
public uint itemid; // id of the item
|
public uint itemid; // id of the item
|
||||||
public uint reference; // referenced TemplateleId
|
public LootStoreItemType type;
|
||||||
public float chance; // chance to drop for both quest and non-quest items, chance to be used for refs
|
public float chance; // chance to drop for both quest and non-quest items, chance to be used for refs
|
||||||
public ushort lootmode;
|
public ushort lootmode;
|
||||||
public bool needs_quest; // quest drop (negative ChanceOrQuestChance in DB)
|
public bool needs_quest; // quest drop (negative ChanceOrQuestChance in DB)
|
||||||
@@ -492,10 +485,10 @@ namespace Game.Loots
|
|||||||
public byte maxcount; // max drop count for the item mincount or Ref multiplicator
|
public byte maxcount; // max drop count for the item mincount or Ref multiplicator
|
||||||
public ConditionsReference conditions; // additional loot condition
|
public ConditionsReference conditions; // additional loot condition
|
||||||
|
|
||||||
public LootStoreItem(uint _itemid, uint _reference, float _chance, bool _needs_quest, ushort _lootmode, byte _groupid, byte _mincount, byte _maxcount)
|
public LootStoreItem(uint _itemid, LootStoreItemType _type, float _chance, bool _needs_quest, ushort _lootmode, byte _groupid, byte _mincount, byte _maxcount)
|
||||||
{
|
{
|
||||||
itemid = _itemid;
|
itemid = _itemid;
|
||||||
reference = _reference;
|
type = _type;
|
||||||
chance = _chance;
|
chance = _chance;
|
||||||
lootmode = _lootmode;
|
lootmode = _lootmode;
|
||||||
needs_quest = _needs_quest;
|
needs_quest = _needs_quest;
|
||||||
@@ -509,66 +502,78 @@ namespace Game.Loots
|
|||||||
if (chance >= 100.0f)
|
if (chance >= 100.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (reference > 0) // reference case
|
switch (type)
|
||||||
return RandomHelper.randChance(chance * (rate ? WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferenced) : 1.0f));
|
{
|
||||||
|
case LootStoreItemType.Item:
|
||||||
|
{
|
||||||
|
ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(itemid);
|
||||||
|
float qualityModifier = pProto != null && rate && LootStoreItem.QualityToRate[(int)pProto.GetQuality()] != WorldCfg.Max ? WorldConfig.GetFloatValue(LootStoreItem.QualityToRate[(int)pProto.GetQuality()]) : 1.0f;
|
||||||
|
return RandomHelper.randChance(chance * qualityModifier);
|
||||||
|
}
|
||||||
|
case LootStoreItemType.Reference:
|
||||||
|
return RandomHelper.randChance(chance * (rate ? WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferenced) : 1.0f));
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
ItemTemplate pProto = Global.ObjectMgr.GetItemTemplate(itemid);
|
return false;
|
||||||
|
|
||||||
float qualityModifier = pProto != null && rate ? WorldConfig.GetFloatValue(qualityToRate[(int)pProto.GetQuality()]) : 1.0f;
|
|
||||||
|
|
||||||
return RandomHelper.randChance(chance * qualityModifier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsValid(LootStore store, uint entry)
|
public bool IsValid(LootStore store, uint entry)
|
||||||
{
|
{
|
||||||
if (mincount == 0)
|
if (mincount == 0)
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: wrong mincount ({3}) - skipped", store.GetName(), entry, itemid, reference);
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: wrong mincount ({3}) - skipped", store.GetName(), entry, itemid, mincount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reference == 0) // item (quest or non-quest) entry, maybe grouped
|
switch (type)
|
||||||
{
|
{
|
||||||
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemid);
|
case LootStoreItemType.Item:
|
||||||
if (proto == null)
|
ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(itemid);
|
||||||
{
|
if (proto == null)
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: item does not exist - skipped", store.GetName(), entry, itemid);
|
{
|
||||||
return false;
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: item does not exist - skipped", store.GetName(), entry, itemid);
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
|
if (chance == 0 && groupid == 0) // Zero chance is allowed for grouped entries only
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: equal-chanced grouped entry, but group not defined - skipped", store.GetName(), entry, itemid);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chance != 0 && chance < 0.000001f) // loot with low chance
|
if (chance != 0 && chance < 0.000001f) // loot with low chance
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: low chance ({3}) - skipped",
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: low chance ({3}) - skipped",
|
||||||
store.GetName(), entry, itemid, chance);
|
store.GetName(), entry, itemid, chance);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxcount < mincount) // wrong max count
|
if (maxcount < mincount) // wrong max count
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: max count ({3}) less that min count ({4}) - skipped", store.GetName(), entry, itemid, maxcount, reference);
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: max count ({3}) less that min count ({4}) - skipped", store.GetName(), entry, itemid, maxcount, mincount);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LootStoreItemType.Reference:
|
||||||
|
if (needs_quest)
|
||||||
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid);
|
||||||
|
else if (chance == 0) // no chance for the reference
|
||||||
|
{
|
||||||
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.outError(LogFilter.Sql, $"Table '{store.GetName()}' Entry {entry} Item {itemid}: invalid ItemType {type}, skipped");
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
else // mincountOrRef < 0
|
|
||||||
{
|
|
||||||
if (needs_quest)
|
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: quest chance will be treated as non-quest chance", store.GetName(), entry, itemid);
|
|
||||||
else if (chance == 0) // no chance for the reference
|
|
||||||
{
|
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} item {2}: zero chance is specified for a reference, skipped", store.GetName(), entry, itemid);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true; // Referenced template existence is checked at whole store level
|
return true; // Referenced template existence is checked at whole store level
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WorldCfg[] qualityToRate = new WorldCfg[7]
|
public static WorldCfg[] QualityToRate =
|
||||||
{
|
[
|
||||||
WorldCfg.RateDropItemPoor, // ITEM_QUALITY_POOR
|
WorldCfg.RateDropItemPoor, // ITEM_QUALITY_POOR
|
||||||
WorldCfg.RateDropItemNormal, // ITEM_QUALITY_NORMAL
|
WorldCfg.RateDropItemNormal, // ITEM_QUALITY_NORMAL
|
||||||
WorldCfg.RateDropItemUncommon, // ITEM_QUALITY_UNCOMMON
|
WorldCfg.RateDropItemUncommon, // ITEM_QUALITY_UNCOMMON
|
||||||
@@ -576,7 +581,9 @@ namespace Game.Loots
|
|||||||
WorldCfg.RateDropItemEpic, // ITEM_QUALITY_EPIC
|
WorldCfg.RateDropItemEpic, // ITEM_QUALITY_EPIC
|
||||||
WorldCfg.RateDropItemLegendary, // ITEM_QUALITY_LEGENDARY
|
WorldCfg.RateDropItemLegendary, // ITEM_QUALITY_LEGENDARY
|
||||||
WorldCfg.RateDropItemArtifact, // ITEM_QUALITY_ARTIFACT
|
WorldCfg.RateDropItemArtifact, // ITEM_QUALITY_ARTIFACT
|
||||||
};
|
WorldCfg.Max, // ITEM_QUALITY_HEIRLOOM
|
||||||
|
WorldCfg.Max, // ITEM_QUALITY_WOW_TOKEN
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public class LootStore
|
public class LootStore
|
||||||
@@ -590,52 +597,53 @@ namespace Game.Loots
|
|||||||
|
|
||||||
void Verify()
|
void Verify()
|
||||||
{
|
{
|
||||||
foreach (var i in m_LootTemplates)
|
foreach (var (lootId, lootTemplate) in m_LootTemplates)
|
||||||
i.Value.Verify(this, i.Key);
|
lootTemplate.Verify(this, lootId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint LoadAndCollectLootIds(out List<uint> lootIdSet)
|
public uint LoadAndCollectLootIds(out List<uint> lootIdSet)
|
||||||
{
|
{
|
||||||
uint count = LoadLootTable();
|
uint count = LoadLootTable();
|
||||||
lootIdSet = new List<uint>();
|
lootIdSet = new(m_LootTemplates.Select(tab => tab.Key));
|
||||||
|
|
||||||
foreach (var tab in m_LootTemplates)
|
|
||||||
lootIdSet.Add(tab.Key);
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckLootRefs(List<uint> ref_set = null)
|
public void CheckLootRefs(List<uint> ref_set = null)
|
||||||
{
|
{
|
||||||
foreach (var pair in m_LootTemplates)
|
foreach (var (_, lootTemplate) in m_LootTemplates)
|
||||||
pair.Value.CheckLootRefs(m_LootTemplates, ref_set);
|
lootTemplate.CheckLootRefs(m_LootTemplates, ref_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReportUnusedIds(List<uint> lootIdSet)
|
public void ReportUnusedIds(List<uint> lootIdSet)
|
||||||
{
|
{
|
||||||
// all still listed ids isn't referenced
|
// all still listed ids isn't referenced
|
||||||
foreach (var id in lootIdSet)
|
foreach (var lootId in lootIdSet)
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} isn't {2} and not referenced from loot, and then useless.", GetName(), id, GetEntryName());
|
Log.outError(LogFilter.Sql, $"Table '{GetName()}' entry {lootId} isn't {GetEntryName()} and not referenced from loot, and then useless.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReportNonExistingId(uint lootId, uint ownerId)
|
public void ReportNonExistingId(uint lootId, uint ownerId)
|
||||||
{
|
{
|
||||||
Log.outError(LogFilter.Sql, "Table '{0}' Entry {1} does not exist but it is used by {2} {3}", GetName(), lootId, GetEntryName(), ownerId);
|
Log.outError(LogFilter.Sql, "Table '{0}' Entry {1} does not exist but it is used by {2} {3}", GetName(), lootId, GetEntryName(), ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HaveLootFor(uint loot_id) { return m_LootTemplates.ContainsKey(loot_id); }
|
public bool HaveLootFor(uint loot_id) { return m_LootTemplates.ContainsKey(loot_id); }
|
||||||
|
|
||||||
public bool HaveQuestLootFor(uint loot_id)
|
public bool HaveQuestLootFor(uint loot_id)
|
||||||
{
|
{
|
||||||
var lootTemplate = m_LootTemplates.LookupByKey(loot_id);
|
|
||||||
if (lootTemplate == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// scan loot for quest items
|
// scan loot for quest items
|
||||||
return lootTemplate.HasQuestDrop(m_LootTemplates);
|
LootTemplate lootTemplate = m_LootTemplates.LookupByKey(loot_id);
|
||||||
|
if (lootTemplate != null)
|
||||||
|
return lootTemplate.HasQuestDrop(m_LootTemplates);
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HaveQuestLootForPlayer(uint loot_id, Player player)
|
public bool HaveQuestLootForPlayer(uint loot_id, Player player)
|
||||||
{
|
{
|
||||||
var tab = m_LootTemplates.LookupByKey(loot_id);
|
LootTemplate lootTemplate = m_LootTemplates.LookupByKey(loot_id);
|
||||||
if (tab != null)
|
if (lootTemplate != null && lootTemplate.HasQuestDropForPlayer(m_LootTemplates, player))
|
||||||
if (tab.HasQuestDropForPlayer(m_LootTemplates, player))
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -659,8 +667,8 @@ namespace Game.Loots
|
|||||||
// Clearing store (for reloading case)
|
// Clearing store (for reloading case)
|
||||||
Clear();
|
Clear();
|
||||||
|
|
||||||
// 0 1 2 3 4 5 6 7 8
|
// 0 1 2 3 4 5 6 7 8
|
||||||
SQLResult result = DB.World.Query("SELECT Entry, Item, Reference, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {0}", GetName());
|
SQLResult result = DB.World.Query($"SELECT Entry, ItemType, Item, Chance, QuestRequired, LootMode, GroupId, MinCount, MaxCount FROM {GetName()}");
|
||||||
if (result.IsEmpty())
|
if (result.IsEmpty())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -668,8 +676,8 @@ namespace Game.Loots
|
|||||||
do
|
do
|
||||||
{
|
{
|
||||||
uint entry = result.Read<uint>(0);
|
uint entry = result.Read<uint>(0);
|
||||||
uint item = result.Read<uint>(1);
|
LootStoreItemType type = (LootStoreItemType)result.Read<sbyte>(1);
|
||||||
uint reference = result.Read<uint>(2);
|
uint item = result.Read<uint>(2);
|
||||||
float chance = result.Read<float>(3);
|
float chance = result.Read<float>(3);
|
||||||
bool needsquest = result.Read<bool>(4);
|
bool needsquest = result.Read<bool>(4);
|
||||||
ushort lootmode = result.Read<ushort>(5);
|
ushort lootmode = result.Read<ushort>(5);
|
||||||
@@ -677,7 +685,7 @@ namespace Game.Loots
|
|||||||
byte mincount = result.Read<byte>(7);
|
byte mincount = result.Read<byte>(7);
|
||||||
byte maxcount = result.Read<byte>(8);
|
byte maxcount = result.Read<byte>(8);
|
||||||
|
|
||||||
LootStoreItem storeitem = new(item, reference, chance, needsquest, lootmode, groupid, mincount, maxcount);
|
LootStoreItem storeitem = new(item, type, chance, needsquest, lootmode, groupid, mincount, maxcount);
|
||||||
|
|
||||||
if (!storeitem.IsValid(this, entry)) // Validity checks
|
if (!storeitem.IsValid(this, entry)) // Validity checks
|
||||||
continue;
|
continue;
|
||||||
@@ -712,7 +720,7 @@ namespace Game.Loots
|
|||||||
{
|
{
|
||||||
public void AddEntry(LootStoreItem item)
|
public void AddEntry(LootStoreItem item)
|
||||||
{
|
{
|
||||||
if (item.groupid > 0 && item.reference == 0) // Group
|
if (item.groupid > 0 && item.type != LootStoreItemType.Reference) // Group
|
||||||
{
|
{
|
||||||
if (!Groups.ContainsKey(item.groupid - 1))
|
if (!Groups.ContainsKey(item.groupid - 1))
|
||||||
Groups[item.groupid - 1] = new LootGroup();
|
Groups[item.groupid - 1] = new LootGroup();
|
||||||
@@ -740,40 +748,41 @@ namespace Game.Loots
|
|||||||
// Rolling non-grouped items
|
// Rolling non-grouped items
|
||||||
foreach (var item in Entries)
|
foreach (var item in Entries)
|
||||||
{
|
{
|
||||||
if (!Convert.ToBoolean(item.lootmode & lootMode)) // Do not add if mode mismatch
|
if ((item.lootmode & lootMode) == 0) // Do not add if mode mismatch
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!item.Roll(rate))
|
if (!item.Roll(rate))
|
||||||
continue; // Bad luck for the entry
|
continue; // Bad luck for the entry
|
||||||
|
|
||||||
if (item.reference > 0) // References processing
|
switch (item.type)
|
||||||
{
|
{
|
||||||
LootTemplate Referenced = LootStorage.Reference.GetLootFor(item.reference);
|
case LootStoreItemType.Item:
|
||||||
if (Referenced == null)
|
// Plain entries (not a reference, not grouped)
|
||||||
continue; // Error message already printed at loading stage
|
// Chance is already checked, just add
|
||||||
|
if (personalLooter == null
|
||||||
|
|| LootItem.AllowedForPlayer(personalLooter, null, item.itemid, item.needs_quest,
|
||||||
|
!item.needs_quest || Global.ObjectMgr.GetItemTemplate(item.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
||||||
|
true, item.conditions))
|
||||||
|
loot.AddItem(item);
|
||||||
|
break;
|
||||||
|
case LootStoreItemType.Reference:
|
||||||
|
LootTemplate Referenced = LootStorage.Reference.GetLootFor(item.itemid);
|
||||||
|
if (Referenced == null)
|
||||||
|
continue; // Error message already printed at loading stage
|
||||||
|
|
||||||
uint maxcount = (uint)(item.maxcount * WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferencedAmount));
|
uint maxcount = (uint)(item.maxcount * WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferencedAmount));
|
||||||
for (uint loop = 0; loop < maxcount; ++loop) // Ref multiplicator
|
for (uint loop = 0; loop < maxcount; ++loop) // Ref multiplicator
|
||||||
Referenced.Process(loot, rate, lootMode, item.groupid, personalLooter);
|
Referenced.Process(loot, rate, lootMode, item.groupid, personalLooter);
|
||||||
}
|
|
||||||
else
|
break;
|
||||||
{
|
default:
|
||||||
// Plain entries (not a reference, not grouped)
|
break;
|
||||||
// Chance is already checked, just add
|
|
||||||
if (personalLooter == null
|
|
||||||
|| LootItem.AllowedForPlayer(personalLooter, null, item.itemid, item.needs_quest,
|
|
||||||
!item.needs_quest || Global.ObjectMgr.GetItemTemplate(item.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
|
||||||
true, item.conditions))
|
|
||||||
loot.AddItem(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now processing groups
|
// Now processing groups
|
||||||
foreach (var group in Groups.Values)
|
foreach (var (_, group) in Groups)
|
||||||
{
|
group?.Process(loot, lootMode, personalLooter);
|
||||||
if (group != null)
|
|
||||||
group.Process(loot, lootMode, personalLooter);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ProcessPersonalLoot(Dictionary<Player, Loot> personalLoot, bool rate, ushort lootMode)
|
public void ProcessPersonalLoot(Dictionary<Player, Loot> personalLoot, bool rate, ushort lootMode)
|
||||||
@@ -798,68 +807,75 @@ namespace Game.Loots
|
|||||||
if (!item.Roll(rate))
|
if (!item.Roll(rate))
|
||||||
continue; // Bad luck for the entry
|
continue; // Bad luck for the entry
|
||||||
|
|
||||||
if (item.reference > 0) // References processing
|
switch (item.type)
|
||||||
{
|
{
|
||||||
LootTemplate referenced = LootStorage.Reference.GetLootFor(item.reference);
|
case LootStoreItemType.Item:
|
||||||
if (referenced == null)
|
|
||||||
continue; // Error message already printed at loading stage
|
|
||||||
|
|
||||||
uint maxcount = (uint)((float)item.maxcount * WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferencedAmount));
|
|
||||||
List<Player> gotLoot = new();
|
|
||||||
for (uint loop = 0; loop < maxcount; ++loop) // Ref multiplicator
|
|
||||||
{
|
{
|
||||||
var lootersForItem = getLootersForItem(looter => referenced.HasDropForPlayer(looter, item.groupid, true));
|
// Plain entries (not a reference, not grouped)
|
||||||
|
// Chance is already checked, just add
|
||||||
// nobody can loot this, skip it
|
var lootersForItem = getLootersForItem(looter =>
|
||||||
if (lootersForItem.Empty())
|
|
||||||
break;
|
|
||||||
|
|
||||||
var newEnd = lootersForItem.RemoveAll(looter => gotLoot.Contains(looter));
|
|
||||||
|
|
||||||
if (lootersForItem.Count == newEnd)
|
|
||||||
{
|
{
|
||||||
// if we run out of looters this means that there are more items dropped than players
|
return LootItem.AllowedForPlayer(looter, null, item.itemid, item.needs_quest,
|
||||||
// start a new cycle adding one item to everyone
|
!item.needs_quest || Global.ObjectMgr.GetItemTemplate(item.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
||||||
gotLoot.Clear();
|
true, item.conditions);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!lootersForItem.Empty())
|
||||||
|
{
|
||||||
|
Player chosenLooter = lootersForItem.SelectRandom();
|
||||||
|
personalLoot[chosenLooter].AddItem(item);
|
||||||
}
|
}
|
||||||
else
|
break;
|
||||||
lootersForItem.RemoveRange(newEnd, lootersForItem.Count - newEnd);
|
|
||||||
|
|
||||||
Player chosenLooter = lootersForItem.SelectRandom();
|
|
||||||
referenced.Process(personalLoot[chosenLooter], rate, lootMode, item.groupid, chosenLooter);
|
|
||||||
gotLoot.Add(chosenLooter);
|
|
||||||
}
|
}
|
||||||
}
|
case LootStoreItemType.Reference:
|
||||||
else
|
|
||||||
{
|
|
||||||
// Plain entries (not a reference, not grouped)
|
|
||||||
// Chance is already checked, just add
|
|
||||||
var lootersForItem = getLootersForItem(looter =>
|
|
||||||
{
|
{
|
||||||
return LootItem.AllowedForPlayer(looter, null, item.itemid, item.needs_quest,
|
LootTemplate referenced = LootStorage.Reference.GetLootFor(item.itemid);
|
||||||
!item.needs_quest || Global.ObjectMgr.GetItemTemplate(item.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
if (referenced == null)
|
||||||
true, item.conditions);
|
continue; // Error message already printed at loading stage
|
||||||
});
|
|
||||||
|
|
||||||
if (!lootersForItem.Empty())
|
uint maxcount = (uint)((float)item.maxcount * WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferencedAmount));
|
||||||
{
|
List<Player> gotLoot = new();
|
||||||
Player chosenLooter = lootersForItem.SelectRandom();
|
for (uint loop = 0; loop < maxcount; ++loop) // Ref multiplicator
|
||||||
personalLoot[chosenLooter].AddItem(item);
|
{
|
||||||
|
var lootersForItem = getLootersForItem(looter => referenced.HasDropForPlayer(looter, item.groupid, true));
|
||||||
|
|
||||||
|
// nobody can loot this, skip it
|
||||||
|
if (lootersForItem.Empty())
|
||||||
|
break;
|
||||||
|
|
||||||
|
var newEnd = lootersForItem.RemoveAll(looter => gotLoot.Contains(looter));
|
||||||
|
|
||||||
|
if (lootersForItem.Count == newEnd)
|
||||||
|
{
|
||||||
|
// if we run out of looters this means that there are more items dropped than players
|
||||||
|
// start a new cycle adding one item to everyone
|
||||||
|
gotLoot.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lootersForItem.RemoveRange(newEnd, lootersForItem.Count - newEnd);
|
||||||
|
|
||||||
|
Player chosenLooter = lootersForItem.SelectRandom();
|
||||||
|
referenced.Process(personalLoot[chosenLooter], rate, lootMode, item.groupid, chosenLooter);
|
||||||
|
gotLoot.Add(chosenLooter);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Now processing groups
|
// Now processing groups
|
||||||
foreach (LootGroup group in Groups.Values)
|
foreach (LootGroup group in Groups.Values)
|
||||||
{
|
|
||||||
if (group != null)
|
|
||||||
{
|
{
|
||||||
var lootersForGroup = getLootersForItem(looter => group.HasDropForPlayer(looter, true));
|
if (group != null)
|
||||||
|
|
||||||
if (!lootersForGroup.Empty())
|
|
||||||
{
|
{
|
||||||
Player chosenLooter = lootersForGroup.SelectRandom();
|
var lootersForGroup = getLootersForItem(looter => group.HasDropForPlayer(looter, true));
|
||||||
group.Process(personalLoot[chosenLooter], lootMode);
|
|
||||||
|
if (!lootersForGroup.Empty())
|
||||||
|
{
|
||||||
|
Player chosenLooter = lootersForGroup.SelectRandom();
|
||||||
|
group.Process(personalLoot[chosenLooter], lootMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -882,22 +898,28 @@ namespace Game.Loots
|
|||||||
// Checking non-grouped entries
|
// Checking non-grouped entries
|
||||||
foreach (LootStoreItem lootStoreItem in Entries)
|
foreach (LootStoreItem lootStoreItem in Entries)
|
||||||
{
|
{
|
||||||
if (lootStoreItem.reference > 0) // References processing
|
switch (lootStoreItem.type)
|
||||||
{
|
{
|
||||||
LootTemplate referenced = LootStorage.Reference.GetLootFor(lootStoreItem.reference);
|
case LootStoreItemType.Item:
|
||||||
if (referenced == null)
|
if (LootItem.AllowedForPlayer(player, null, lootStoreItem.itemid, lootStoreItem.needs_quest,
|
||||||
continue; // Error message already printed at loading stage
|
!lootStoreItem.needs_quest || Global.ObjectMgr.GetItemTemplate(lootStoreItem.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
||||||
if (referenced.HasDropForPlayer(player, lootStoreItem.groupid, strictUsabilityCheck))
|
strictUsabilityCheck, lootStoreItem.conditions))
|
||||||
return true;
|
return true; // active quest drop found
|
||||||
|
break;
|
||||||
|
case LootStoreItemType.Reference:
|
||||||
|
LootTemplate referenced = LootStorage.Reference.GetLootFor(lootStoreItem.itemid);
|
||||||
|
if (referenced == null)
|
||||||
|
continue; // Error message already printed at loading stage
|
||||||
|
if (referenced.HasDropForPlayer(player, lootStoreItem.groupid, strictUsabilityCheck))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (LootItem.AllowedForPlayer(player, null, lootStoreItem.itemid, lootStoreItem.needs_quest,
|
|
||||||
!lootStoreItem.needs_quest || Global.ObjectMgr.GetItemTemplate(lootStoreItem.itemid).HasFlag(ItemFlagsCustom.FollowLootRules),
|
|
||||||
strictUsabilityCheck, lootStoreItem.conditions))
|
|
||||||
return true; // active quest drop found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now checking groups
|
// Now checking groups
|
||||||
foreach (LootGroup group in Groups.Values)
|
foreach (var (_, group) in Groups)
|
||||||
if (group != null && group.HasDropForPlayer(player, strictUsabilityCheck))
|
if (group != null && group.HasDropForPlayer(player, strictUsabilityCheck))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@@ -932,21 +954,27 @@ namespace Game.Loots
|
|||||||
|
|
||||||
foreach (var item in Entries)
|
foreach (var item in Entries)
|
||||||
{
|
{
|
||||||
if (item.reference > 0) // References
|
switch (item.type)
|
||||||
{
|
{
|
||||||
var Referenced = store.LookupByKey(item.reference);
|
case LootStoreItemType.Item:
|
||||||
if (Referenced == null)
|
if (item.needs_quest)
|
||||||
continue; // Error message [should be] already printed at loading stage
|
return true; // quest drop found
|
||||||
if (Referenced.HasQuestDrop(store, item.groupid))
|
break;
|
||||||
return true;
|
case LootStoreItemType.Reference:
|
||||||
|
var Referenced = store.LookupByKey(item.itemid);
|
||||||
|
if (Referenced == null)
|
||||||
|
continue; // Error message [should be] already printed at loading stage
|
||||||
|
if (Referenced.HasQuestDrop(store, item.groupid))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (item.needs_quest)
|
|
||||||
return true; // quest drop found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now processing groups
|
// Now processing groups
|
||||||
foreach (var group in Groups.Values)
|
foreach (var (_, group) in Groups)
|
||||||
if (group.HasQuestDrop())
|
if (group != null && group.HasQuestDrop())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -968,21 +996,27 @@ namespace Game.Loots
|
|||||||
// Checking non-grouped entries
|
// Checking non-grouped entries
|
||||||
foreach (var item in Entries)
|
foreach (var item in Entries)
|
||||||
{
|
{
|
||||||
if (item.reference > 0) // References processing
|
switch (item.type)
|
||||||
{
|
{
|
||||||
var Referenced = store.LookupByKey(item.reference);
|
case LootStoreItemType.Item:
|
||||||
if (Referenced == null)
|
if (player.HasQuestForItem(item.itemid))
|
||||||
continue; // Error message already printed at loading stage
|
return true; // active quest drop found
|
||||||
if (Referenced.HasQuestDropForPlayer(store, player, item.groupid))
|
break;
|
||||||
return true;
|
case LootStoreItemType.Reference:
|
||||||
|
var Referenced = store.LookupByKey(item.itemid);
|
||||||
|
if (Referenced == null)
|
||||||
|
continue; // Error message already printed at loading stage
|
||||||
|
if (Referenced.HasQuestDropForPlayer(store, player, item.groupid))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (player.HasQuestForItem(item.itemid))
|
|
||||||
return true; // active quest drop found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now checking groups
|
// Now checking groups
|
||||||
foreach (var group in Groups.Values)
|
foreach (var (_, group) in Groups)
|
||||||
if (group.HasQuestDropForPlayer(player))
|
if (group != null && group.HasQuestDropForPlayer(player))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -1000,17 +1034,17 @@ namespace Game.Loots
|
|||||||
{
|
{
|
||||||
foreach (var item in Entries)
|
foreach (var item in Entries)
|
||||||
{
|
{
|
||||||
if (item.reference > 0)
|
if (item.type == LootStoreItemType.Reference)
|
||||||
{
|
{
|
||||||
if (LootStorage.Reference.GetLootFor(item.reference) == null)
|
if (LootStorage.Reference.GetLootFor(item.itemid) == null)
|
||||||
LootStorage.Reference.ReportNonExistingId(item.reference, item.itemid);
|
LootStorage.Reference.ReportNonExistingId(item.itemid, item.itemid);
|
||||||
else if (ref_set != null)
|
else if (ref_set != null)
|
||||||
ref_set.Remove(item.reference);
|
ref_set.Remove(item.itemid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var group in Groups.Values)
|
foreach (var (_, group) in Groups)
|
||||||
group.CheckLootRefs(store, ref_set);
|
group?.CheckLootRefs(store, ref_set);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LinkConditions(ConditionId id, ConditionsReference reference)
|
public bool LinkConditions(ConditionId id, ConditionsReference reference)
|
||||||
@@ -1079,26 +1113,22 @@ namespace Game.Loots
|
|||||||
|
|
||||||
public bool HasQuestDrop()
|
public bool HasQuestDrop()
|
||||||
{
|
{
|
||||||
foreach (var i in ExplicitlyChanced)
|
if (ExplicitlyChanced.Any(item => item.needs_quest))
|
||||||
if (i.needs_quest)
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
foreach (var i in EqualChanced)
|
if (EqualChanced.Any(item => item.needs_quest))
|
||||||
if (i.needs_quest)
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasQuestDropForPlayer(Player player)
|
public bool HasQuestDropForPlayer(Player player)
|
||||||
{
|
{
|
||||||
foreach (var i in ExplicitlyChanced)
|
if (ExplicitlyChanced.Any(item => player.HasQuestForItem(item.itemid)))
|
||||||
if (player.HasQuestForItem(i.itemid))
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
foreach (var i in EqualChanced)
|
if (EqualChanced.Any(item => player.HasQuestForItem(item.itemid)))
|
||||||
if (player.HasQuestForItem(i.itemid))
|
return true;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1109,16 +1139,18 @@ namespace Game.Loots
|
|||||||
if (item != null)
|
if (item != null)
|
||||||
loot.AddItem(item);
|
loot.AddItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
float RawTotalChance()
|
float RawTotalChance()
|
||||||
{
|
{
|
||||||
float result = 0;
|
float result = 0;
|
||||||
|
|
||||||
foreach (var i in ExplicitlyChanced)
|
foreach (var item in ExplicitlyChanced)
|
||||||
if (!i.needs_quest)
|
if (!item.needs_quest)
|
||||||
result += i.chance;
|
result += item.chance;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TotalChance()
|
float TotalChance()
|
||||||
{
|
{
|
||||||
float result = RawTotalChance();
|
float result = RawTotalChance();
|
||||||
@@ -1139,31 +1171,34 @@ namespace Game.Loots
|
|||||||
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} group {2} has items with chance=0% but group total chance >= 100% ({3})", lootstore.GetName(), id, group_id, chance);
|
Log.outError(LogFilter.Sql, "Table '{0}' entry {1} group {2} has items with chance=0% but group total chance >= 100% ({3})", lootstore.GetName(), id, group_id, chance);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CheckLootRefs(LootTemplateMap store, List<uint> ref_set)
|
public void CheckLootRefs(LootTemplateMap store, List<uint> ref_set)
|
||||||
{
|
{
|
||||||
foreach (var item in ExplicitlyChanced)
|
foreach (var item in ExplicitlyChanced)
|
||||||
{
|
{
|
||||||
if (item.reference > 0)
|
if (item.type == LootStoreItemType.Reference)
|
||||||
{
|
{
|
||||||
if (LootStorage.Reference.GetLootFor(item.reference) == null)
|
if (LootStorage.Reference.GetLootFor(item.itemid) == null)
|
||||||
LootStorage.Reference.ReportNonExistingId(item.reference, item.itemid);
|
LootStorage.Reference.ReportNonExistingId(item.itemid, item.itemid);
|
||||||
else if (ref_set != null)
|
else if (ref_set != null)
|
||||||
ref_set.Remove(item.reference);
|
ref_set.Remove(item.itemid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in EqualChanced)
|
foreach (var item in EqualChanced)
|
||||||
{
|
{
|
||||||
if (item.reference > 0)
|
if (item.type == LootStoreItemType.Reference)
|
||||||
{
|
{
|
||||||
if (LootStorage.Reference.GetLootFor(item.reference) == null)
|
if (LootStorage.Reference.GetLootFor(item.itemid) == null)
|
||||||
LootStorage.Reference.ReportNonExistingId(item.reference, item.itemid);
|
LootStorage.Reference.ReportNonExistingId(item.itemid, item.itemid);
|
||||||
else if (ref_set != null)
|
else if (ref_set != null)
|
||||||
ref_set.Remove(item.reference);
|
ref_set.Remove(item.itemid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LootStoreItemList GetExplicitlyChancedItemList() { return ExplicitlyChanced; }
|
public LootStoreItemList GetExplicitlyChancedItemList() { return ExplicitlyChanced; }
|
||||||
|
|
||||||
public LootStoreItemList GetEqualChancedItemList() { return EqualChanced; }
|
public LootStoreItemList GetEqualChancedItemList() { return EqualChanced; }
|
||||||
|
|
||||||
LootStoreItemList ExplicitlyChanced = new(); // Entries with chances defined in DB
|
LootStoreItemList ExplicitlyChanced = new(); // Entries with chances defined in DB
|
||||||
|
|||||||
Reference in New Issue
Block a user