Files
CypherCore/Source/Game/Spells/Skills/SkillDiscovery.cs
T
2023-01-18 17:25:32 -05:00

245 lines
9.9 KiB
C#

// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants;
using Framework.Database;
using Game.DataStorage;
using Game.Entities;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Game.Spells
{
public class SkillDiscovery
{
public static void LoadSkillDiscoveryTable()
{
uint oldMSTime = Time.GetMSTime();
SkillDiscoveryStorage.Clear(); // need for reload
// 0 1 2 3
SQLResult result = DB.World.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
if (result.IsEmpty())
{
Log.outInfo(LogFilter.ServerLoading, "Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty.");
return;
}
uint count = 0;
StringBuilder ssNonDiscoverableEntries = new();
List<uint> reportedReqSpells = new();
do
{
uint spellId = result.Read<uint>(0);
int reqSkillOrSpell = result.Read<int>(1);
uint reqSkillValue = result.Read<uint>(2);
float chance = result.Read<float>(3);
if (chance <= 0) // chance
{
ssNonDiscoverableEntries.AppendFormat("spellId = {0} reqSkillOrSpell = {1} reqSkillValue = {2} chance = {3} (chance problem)\n", spellId, reqSkillOrSpell, reqSkillValue, chance);
continue;
}
if (reqSkillOrSpell > 0) // spell case
{
uint absReqSkillOrSpell = (uint)reqSkillOrSpell;
SpellInfo reqSpellInfo = Global.SpellMgr.GetSpellInfo(absReqSkillOrSpell, Difficulty.None);
if (reqSpellInfo == null)
{
if (!reportedReqSpells.Contains(absReqSkillOrSpell))
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) have not existed spell (ID: {1}) in `reqSpell` field in `skill_discovery_template` table", spellId, reqSkillOrSpell);
reportedReqSpells.Add(absReqSkillOrSpell);
}
continue;
}
// mechanic discovery
if (reqSpellInfo.Mechanic != Mechanics.Discovery &&
// explicit discovery ability
!reqSpellInfo.IsExplicitDiscovery())
{
if (!reportedReqSpells.Contains(absReqSkillOrSpell))
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc" +
" and not 100%% chance random discovery ability but listed for spellId {1} (and maybe more) in `skill_discovery_template` table",
absReqSkillOrSpell, spellId);
reportedReqSpells.Add(absReqSkillOrSpell);
}
continue;
}
SkillDiscoveryStorage.Add(reqSkillOrSpell, new SkillDiscoveryEntry(spellId, reqSkillValue, chance));
}
else if (reqSkillOrSpell == 0) // skill case
{
var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spellId);
if (bounds.Empty())
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table", spellId);
continue;
}
foreach (var _spell_idx in bounds)
SkillDiscoveryStorage.Add(-(int)_spell_idx.SkillLine, new SkillDiscoveryEntry(spellId, reqSkillValue, chance));
}
else
{
Log.outError(LogFilter.Sql, "Spell (ID: {0}) have negative value in `reqSpell` field in `skill_discovery_template` table", spellId);
continue;
}
++count;
}
while (result.NextRow());
if (ssNonDiscoverableEntries.Length != 0)
Log.outError(LogFilter.Sql, "Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n{0}", ssNonDiscoverableEntries.ToString());
// report about empty data for explicit discovery spells
foreach (SpellNameRecord spellNameEntry in CliDB.SpellNameStorage.Values)
{
SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(spellNameEntry.Id, Difficulty.None);
if (spellEntry == null)
continue;
// skip not explicit discovery spells
if (!spellEntry.IsExplicitDiscovery())
continue;
if (!SkillDiscoveryStorage.ContainsKey((int)spellEntry.Id))
Log.outError(LogFilter.Sql, "Spell (ID: {0}) is 100% chance random discovery ability but not have data in `skill_discovery_template` table", spellEntry.Id);
}
Log.outInfo(LogFilter.ServerLoading, "Loaded {0} skill discovery definitions in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime));
}
public static uint GetExplicitDiscoverySpell(uint spellId, Player player)
{
// explicit discovery spell chances (always success if case exist)
// in this case we have both skill and spell
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (tab.Empty())
return 0;
var bounds = Global.SpellMgr.GetSkillLineAbilityMapBounds(spellId);
uint skillvalue = !bounds.Empty() ? (uint)player.GetSkillValue((SkillType)bounds.FirstOrDefault().SkillLine) : 0;
float full_chance = 0;
foreach (var item_iter in tab)
if (item_iter.reqSkillValue <= skillvalue)
if (!player.HasSpell(item_iter.spellId))
full_chance += item_iter.chance;
float rate = full_chance / 100.0f;
float roll = (float)RandomHelper.randChance() * rate; // roll now in range 0..full_chance
foreach (var item_iter in tab)
{
if (item_iter.reqSkillValue > skillvalue)
continue;
if (player.HasSpell(item_iter.spellId))
continue;
if (item_iter.chance > roll)
return item_iter.spellId;
roll -= item_iter.chance;
}
return 0;
}
public static bool HasDiscoveredAllSpells(uint spellId, Player player)
{
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (tab.Empty())
return true;
foreach (var item_iter in tab)
if (!player.HasSpell(item_iter.spellId))
return false;
return true;
}
public static bool HasDiscoveredAnySpell(uint spellId, Player player)
{
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (tab.Empty())
return false;
foreach (var item_iter in tab)
if (player.HasSpell(item_iter.spellId))
return true;
return false;
}
public static uint GetSkillDiscoverySpell(uint skillId, uint spellId, Player player)
{
uint skillvalue = skillId != 0 ? (uint)player.GetSkillValue((SkillType)skillId) : 0;
// check spell case
var tab = SkillDiscoveryStorage.LookupByKey((int)spellId);
if (!tab.Empty())
{
foreach (var item_iter in tab)
{
if (RandomHelper.randChance(item_iter.chance * WorldConfig.GetFloatValue(WorldCfg.RateSkillDiscovery)) &&
item_iter.reqSkillValue <= skillvalue &&
!player.HasSpell(item_iter.spellId))
return item_iter.spellId;
}
return 0;
}
if (skillId == 0)
return 0;
// check skill line case
tab = SkillDiscoveryStorage.LookupByKey(-(int)skillId);
if (!tab.Empty())
{
foreach (var item_iter in tab)
{
if (RandomHelper.randChance(item_iter.chance * WorldConfig.GetFloatValue(WorldCfg.RateSkillDiscovery)) &&
item_iter.reqSkillValue <= skillvalue &&
!player.HasSpell(item_iter.spellId))
return item_iter.spellId;
}
return 0;
}
return 0;
}
static MultiMap<int, SkillDiscoveryEntry> SkillDiscoveryStorage = new();
}
public class SkillDiscoveryEntry
{
public SkillDiscoveryEntry(uint _spellId = 0, uint req_skill_val = 0, float _chance = 0)
{
spellId = _spellId;
reqSkillValue = req_skill_val;
chance = _chance;
}
public uint spellId; // discavered spell
public uint reqSkillValue; // skill level limitation
public float chance; // chance
}
}