You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
561 lines
20 KiB
561 lines
20 KiB
1 month ago
|
using System;
|
||
|
using System.Linq;
|
||
|
using ProtoCSStruct;
|
||
|
using Sog;
|
||
|
|
||
|
namespace Game
|
||
|
{
|
||
|
/**
|
||
|
* 玩家天赋系统
|
||
|
*/
|
||
|
public static class TalentSvc
|
||
|
{
|
||
|
private const int DefaultTalentLevel = 1;
|
||
|
|
||
|
|
||
|
public static void OnPlayerBorn(PlayerOnGame player)
|
||
|
{
|
||
|
ref var talent = ref player.RoleData.Talent;
|
||
|
talent.Clear();
|
||
|
var talentPage = new DbTalentPageData { PageIndex = (int)PageIndex.Normal };
|
||
|
talent.TalentPage.CopyFrom(ref talentPage);
|
||
|
var peakPage = new DbTalentPageData { PageIndex = (int)PageIndex.Peak };
|
||
|
talent.PeakData.CopyFrom(ref peakPage);
|
||
|
talent.TalentPage.Point = DefaultTalentLevel;
|
||
|
player.MakeDirty();
|
||
|
}
|
||
|
|
||
|
public static void OnTalentOption(PlayerOnGame player, StructPacket packet)
|
||
|
{
|
||
|
var res = new CSTalentOptionsRes();
|
||
|
var unlock = SystemUnlockSvc.IsUnlockSys(player, (int)SystemUnlockId.Talent);
|
||
|
if (!unlock)
|
||
|
{
|
||
|
res.Ret = CSErrCode.SysNotUnlock;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentOptionsRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var change = ChapterSvc.CanChangeTalentOrEquip(player);
|
||
|
if (!change)
|
||
|
{
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentOptionsRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ref var req = ref packet.GetMessage<CSTalentOptionsReq>();
|
||
|
res = new CSTalentOptionsRes()
|
||
|
{
|
||
|
Option = req.Option,
|
||
|
PageIndex = req.PageIndex
|
||
|
};
|
||
|
switch (req.PageIndex)
|
||
|
{
|
||
|
case (int)PageIndex.Peak:
|
||
|
{
|
||
|
ref var data = ref player.RoleData.Talent.PeakData;
|
||
|
player.RoleData.Talent.PeakData.Items.Clear();
|
||
|
TalentPeakSvc.Recalculate(player);
|
||
|
res.Data.CopyFrom(ref data);
|
||
|
}
|
||
|
break;
|
||
|
case (int)PageIndex.Normal:
|
||
|
{
|
||
|
ref var data = ref player.RoleData.Talent.TalentPage;
|
||
|
data.Items.Clear();
|
||
|
data.IsActive = true;
|
||
|
res.Ret = CSErrCode.None;
|
||
|
RecalculateTalent(ref data);
|
||
|
res.Data.CopyFrom(ref data);
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
TraceLog.Error("pageIndex not found {0}", req.PageIndex);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
player.MakeDirty();
|
||
|
player.SendToClient((int)CSGameMsgID.TalentOptionsRes, ref res);
|
||
|
}
|
||
|
|
||
|
//分配/减少天赋点数
|
||
|
public static void OnActiveOption(PlayerOnGame player, StructPacket packet)
|
||
|
{
|
||
|
ref var req = ref packet.GetMessage<CSTalentActiveReq>();
|
||
|
var talentId = req.TalentId;
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(talentId);
|
||
|
if (desc == null)
|
||
|
{
|
||
|
ref var res = ref CSTalentActiveRes.Parser.GetMessageClear();
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var pageIndex = (PageIndex)req.PageIndex;
|
||
|
switch (pageIndex)
|
||
|
{
|
||
|
case PageIndex.Normal:
|
||
|
OnTalentActiveOption(player, req);
|
||
|
break;
|
||
|
case PageIndex.Peak:
|
||
|
TalentPeakSvc.OnActiveOption(player, req);
|
||
|
break;
|
||
|
default:
|
||
|
TraceLog.Error("pageIndex not found {0}", pageIndex);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static void OnTalentActiveOption(PlayerOnGame player, CSTalentActiveReq req)
|
||
|
{
|
||
|
ref var res = ref CSTalentActiveRes.Parser.GetMessageClear();
|
||
|
var change = ChapterSvc.CanChangeTalentOrEquip(player);
|
||
|
if (!change)
|
||
|
{
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ref var talentPageData = ref player.RoleData.Talent.TalentPage;
|
||
|
if (!talentPageData.IsActive)
|
||
|
{
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!TalentHelper.IsCurrentPageTalent(PageIndex.Normal, req.TalentId))
|
||
|
{
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
;
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(req.TalentId);
|
||
|
var def = desc.type == TalentType.Core && desc.LevelPoint[0] == 0;
|
||
|
if (def)
|
||
|
{
|
||
|
res.Ret = CSErrCode.Fail;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var ret = req.Backup
|
||
|
? TalentBackup(ref talentPageData, req.TalentId)
|
||
|
: TalentActive(ref talentPageData, req.TalentId);
|
||
|
if (ret != CSErrCode.None)
|
||
|
{
|
||
|
res.Ret = ret;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
RecalculateTalent(ref talentPageData);
|
||
|
player.MakeDirty();
|
||
|
res.PageIndex = (int)PageIndex.Normal;
|
||
|
res.Data = talentPageData;
|
||
|
res.Ret = ret;
|
||
|
player.SendToClient((int)CSGameMsgID.TalentActiveRes, ref res);
|
||
|
}
|
||
|
|
||
|
|
||
|
public static void SyncData(PlayerOnGame player)
|
||
|
{
|
||
|
ref var res = ref CSTalentDataSync.Parser.GetMessageClear();
|
||
|
res.Talent.CopyFrom(ref player.RoleData.Talent);
|
||
|
player.SendToClient((int)CSGameMsgID.TalentDataSync, ref res);
|
||
|
}
|
||
|
|
||
|
|
||
|
private static CSErrCode TalentBackup(ref DbTalentPageData roleData, int descId)
|
||
|
{
|
||
|
ref var talent = ref roleData.Items;
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(descId);
|
||
|
if (desc == null)
|
||
|
{
|
||
|
return CSErrCode.Fail;
|
||
|
}
|
||
|
|
||
|
var index = -1;
|
||
|
for (var t = 0; t < talent.Count; t++)
|
||
|
{
|
||
|
if (talent[t].Id != descId) continue;
|
||
|
index = t;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (index < 0)
|
||
|
{
|
||
|
return CSErrCode.DescNotFound;
|
||
|
}
|
||
|
|
||
|
var level = talent[index].Value;
|
||
|
if (level == 1)
|
||
|
{
|
||
|
talent.RemoveAt(index);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
talent[index].Value -= 1;
|
||
|
}
|
||
|
|
||
|
return CSErrCode.None;
|
||
|
}
|
||
|
|
||
|
|
||
|
private static CSErrCode TalentActive(ref DbTalentPageData talent, int descId)
|
||
|
{
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(descId);
|
||
|
if (!talent.IsActive)
|
||
|
{
|
||
|
return CSErrCode.Fail; //不可以被激活 不是当前系统页签的天赋,需要激活final类型的
|
||
|
}
|
||
|
|
||
|
var index = -1;
|
||
|
var unlockConditionTalentIds = desc.unlockConditionTalent;
|
||
|
for (var i = 0; i < talent.Items.Count; i++)
|
||
|
{
|
||
|
var tempTalentId = talent.Items[i].Id;
|
||
|
var tempDesc = TalentDescMgr.Instance.GetConfig(tempTalentId);
|
||
|
if (desc.lockTalent.Contains(tempTalentId))
|
||
|
{
|
||
|
TraceLog.Error("TalentSvc.Active Talent Lock talentId={0},lockIds={1}", desc.id,
|
||
|
string.Join(",", tempDesc.lockTalent));
|
||
|
return CSErrCode.Fail; //全局流派互斥
|
||
|
}
|
||
|
|
||
|
if (tempTalentId == descId)
|
||
|
{
|
||
|
index = i;
|
||
|
if (talent.Items[i].Value >= desc.Level)
|
||
|
{
|
||
|
TraceLog.Error("TalentSvc.Active Talent Level Max talentId={0}", talent.Items[i].Value);
|
||
|
return CSErrCode.Fail; //超过最大上限了
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (unlockConditionTalentIds.Contains(tempTalentId))
|
||
|
{
|
||
|
//前置天赋
|
||
|
var dk = -1;
|
||
|
for (var j = 0; j < unlockConditionTalentIds.Length; j++)
|
||
|
{
|
||
|
if (unlockConditionTalentIds[j] == tempTalentId)
|
||
|
{
|
||
|
dk = j;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dk >= desc.unlockConditionTalentLevel.Length)
|
||
|
{
|
||
|
dk = desc.unlockConditionTalentLevel.Length - 1; //兼容一下,不然会数组越界
|
||
|
}
|
||
|
|
||
|
if (dk != -1)
|
||
|
{
|
||
|
var level = desc.unlockConditionTalentLevel[dk];
|
||
|
if (talent.Items[i].Value < level)
|
||
|
{
|
||
|
TraceLog.Error("TalentSvc.Active pre talent level low talentId={0},level={1}",
|
||
|
talent.Items[i].Id, level);
|
||
|
return CSErrCode.Fail; //前置条件不满足
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//上一层是否有激活的天赋
|
||
|
var unlockConditionPoint = desc.unlockConditionPoint;
|
||
|
if (unlockConditionPoint > 0)
|
||
|
{
|
||
|
var count = GetPreAllTalentCount(talent, descId);
|
||
|
if (count < unlockConditionPoint)
|
||
|
{
|
||
|
TraceLog.Error("TalentSvc.Active pre talent point low unlock point={0}, cost={1}", count,
|
||
|
unlockConditionPoint);
|
||
|
|
||
|
return CSErrCode.Fail; //前置点数不够
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var costIndex = 0;
|
||
|
if (index >= 0)
|
||
|
{
|
||
|
costIndex = talent.Items[index].Value - 1;
|
||
|
}
|
||
|
|
||
|
if (costIndex >= desc.LevelPoint.Length)
|
||
|
{
|
||
|
costIndex = desc.LevelPoint.Length - 1;
|
||
|
}
|
||
|
|
||
|
var point = desc.LevelPoint[costIndex]; //这一次升级需要的点数
|
||
|
|
||
|
var talentCanUsePoint = GetTalentCanUsePoint(ref talent);
|
||
|
|
||
|
if (point != 0 && talentCanUsePoint < point) //需要的点数不够
|
||
|
{
|
||
|
TraceLog.Error("TalentSvc.Active need talent point low unlock point={0}, cost={1}", point,
|
||
|
talentCanUsePoint);
|
||
|
return CSErrCode.Fail;
|
||
|
}
|
||
|
|
||
|
if (index != -1)
|
||
|
{
|
||
|
talent.Items[index].Value++;
|
||
|
return CSErrCode.None;
|
||
|
}
|
||
|
|
||
|
talent.Items.Add(NewDbTalentOne(desc.id, DefaultTalentLevel));
|
||
|
return CSErrCode.None;
|
||
|
}
|
||
|
|
||
|
|
||
|
private static IDValue32 NewDbTalentOne(int id, int value)
|
||
|
{
|
||
|
return new IDValue32()
|
||
|
{
|
||
|
Id = id,
|
||
|
Value = value,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
public static void AddTalentPoint(PlayerOnGame player, int point)
|
||
|
{
|
||
|
var limits = CommParamDescMgr.Instance.TalentPoint.int_list;
|
||
|
ref var talent = ref player.RoleData.Talent.TalentPage;
|
||
|
var res = new CSTalentOptionsRes();
|
||
|
var left = limits[0];
|
||
|
var right = limits[1];
|
||
|
if (talent.Point < left)
|
||
|
{
|
||
|
talent.Point += point;
|
||
|
res.Data.CopyFrom(ref talent);
|
||
|
res.PageIndex = (int)PageIndex.Normal;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
player.RoleData.Talent.PeakData.Point += point;
|
||
|
player.RoleData.Talent.PeakData.Point = Math.Min(right, player.RoleData.Talent.PeakData.Point);
|
||
|
TalentPeakSvc.Active(player);
|
||
|
res.Data.CopyFrom(ref player.RoleData.Talent.PeakData);
|
||
|
res.PageIndex = (int)PageIndex.Peak;
|
||
|
}
|
||
|
|
||
|
player.SendToClient((int)CSGameMsgID.TalentOptionsRes, ref res);
|
||
|
}
|
||
|
|
||
|
|
||
|
//重新计算激活
|
||
|
private static void RecalculateTalent(ref DbTalentPageData talentPageData)
|
||
|
{
|
||
|
var newTalentData = new DbTalentPageData
|
||
|
{
|
||
|
Point = talentPageData.Point,
|
||
|
IsActive = talentPageData.IsActive,
|
||
|
PageIndex = (int)talentPageData.PageIndex
|
||
|
};
|
||
|
//处理当前页
|
||
|
ProcCurrentTalent(talentPageData, ref newTalentData);
|
||
|
talentPageData.CopyFrom(ref newTalentData);
|
||
|
}
|
||
|
|
||
|
//重算当前激活页天赋
|
||
|
private static void ProcCurrentTalent(DbTalentPageData oldTalentData, ref DbTalentPageData newTalentData)
|
||
|
{
|
||
|
foreach (var kv in TalentDescMgr.Instance.ItemTable)
|
||
|
{
|
||
|
var desc = kv.Value;
|
||
|
if (!TalentHelper.IsCurrentPageTalent(PageIndex.Normal, desc.id)) //是否属于当前页的天赋技能
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var def = desc.type == TalentType.Core && desc.LevelPoint[0] == 0;
|
||
|
if (def) //默认天赋技能
|
||
|
{
|
||
|
newTalentData.Items.Add(NewDbTalentOne(desc.id, DefaultTalentLevel));
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var index = -1;
|
||
|
for (var i = 0; i < oldTalentData.Items.Count; i++)
|
||
|
{
|
||
|
if (oldTalentData.Items[i].Id == desc.id)
|
||
|
|
||
|
{
|
||
|
index = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (index < 0)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var oneData = oldTalentData.Items[index];
|
||
|
var descUnlockConditionPoint = desc.unlockConditionPoint;
|
||
|
var preTalentCount = GetPreAllTalentCount(oldTalentData, oneData.Id);
|
||
|
if (descUnlockConditionPoint > 0 && descUnlockConditionPoint > preTalentCount) //所有前置总点数
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
var preTalentIndex = GetPreTalentIndex(oldTalentData, oneData.Id);
|
||
|
var unlockConditionTalent = desc.unlockConditionTalent;
|
||
|
var unlockConditionLevel = desc.unlockConditionTalentLevel;
|
||
|
|
||
|
var prePathConduction = (preTalentIndex > -1 && unlockConditionTalent.Length > 0) ||
|
||
|
(unlockConditionTalent.Length == 0);
|
||
|
|
||
|
while (preTalentIndex > -1 && unlockConditionTalent.Length > 0) //前置天赋的前置天赋
|
||
|
{
|
||
|
var preOldOne = oldTalentData.Items[preTalentIndex];
|
||
|
var preTalentId = preOldOne.Id;
|
||
|
var preTalentLevel = preOldOne.Value;
|
||
|
var cid = 0;
|
||
|
for (var i = 0; i < unlockConditionTalent.Length; i++)
|
||
|
{
|
||
|
if (unlockConditionTalent[i] == preTalentId)
|
||
|
{
|
||
|
cid = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (cid >= unlockConditionLevel.Length)
|
||
|
{
|
||
|
cid = unlockConditionLevel.Length - 1;
|
||
|
}
|
||
|
|
||
|
var tarLevel = unlockConditionLevel[cid];
|
||
|
prePathConduction = preTalentLevel >= tarLevel;
|
||
|
preTalentIndex = GetPreTalentIndex(oldTalentData, preOldOne.Id);
|
||
|
var temDesc = TalentDescMgr.Instance.GetConfig(preOldOne.Id);
|
||
|
unlockConditionTalent = temDesc.unlockConditionTalent;
|
||
|
unlockConditionLevel = temDesc.unlockConditionTalentLevel;
|
||
|
if (!prePathConduction)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//此时的unlockConditionLevel应该是根天赋,所有路径上的天赋都满足才对(当然巅峰天赋没有上下级,也无所谓)
|
||
|
prePathConduction = prePathConduction && unlockConditionLevel.Length == 0;
|
||
|
|
||
|
if (!prePathConduction)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
newTalentData.Items.Add(oneData); //前置行点数够了
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//剩余天赋点数
|
||
|
private static int GetTalentCanUsePoint(ref DbTalentPageData talentPageData)
|
||
|
{
|
||
|
var allPoint = talentPageData.Point;
|
||
|
var cost = GetAllTalentCostCount(ref talentPageData);
|
||
|
return Math.Max(0, allPoint - cost);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 获取当前页所有消耗的点数
|
||
|
*/
|
||
|
private static int GetAllTalentCostCount(ref DbTalentPageData pageData)
|
||
|
{
|
||
|
var cost = 0; //已消耗的点数
|
||
|
var talents = pageData.Items;
|
||
|
for (var i = 0; i < talents.Count; i++)
|
||
|
{
|
||
|
var id = talents[i].Id;
|
||
|
var level = talents[i].Value;
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(id);
|
||
|
var levelPoint = desc.LevelPoint;
|
||
|
for (var j = 0; j < level; j++)
|
||
|
{
|
||
|
if (j >= levelPoint.Length)
|
||
|
{
|
||
|
cost += levelPoint[^1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cost += levelPoint[j];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return cost;
|
||
|
}
|
||
|
|
||
|
|
||
|
//前置天赋点了多少点
|
||
|
private static int GetPreAllTalentCount(DbTalentPageData talent, int targetId)
|
||
|
{
|
||
|
var preTalentIndex = GetPreTalentIndex(talent, targetId);
|
||
|
if (preTalentIndex < 0) //没有前置天赋
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
var ret = 0;
|
||
|
while (preTalentIndex > 0)
|
||
|
{
|
||
|
var talentOne = talent.Items[preTalentIndex];
|
||
|
var preTalentId = talentOne.Id;
|
||
|
var level = talentOne.Value;
|
||
|
var preDesc = TalentDescMgr.Instance.GetConfig(preTalentId);
|
||
|
var pl = preDesc.LevelPoint;
|
||
|
for (var i = 0; i < level; i++)
|
||
|
{
|
||
|
if (i >= pl.Length)
|
||
|
{
|
||
|
ret += pl[^1];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret += pl[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
preTalentIndex = GetPreTalentIndex(talent, preTalentId);
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
//获取某个天赋的已经点亮的前置天赋索引
|
||
|
private static int GetPreTalentIndex(DbTalentPageData talent, int targetId)
|
||
|
{
|
||
|
var desc = TalentDescMgr.Instance.GetConfig(targetId);
|
||
|
var unlockConditionTalent = desc.unlockConditionTalent;
|
||
|
var index = -1;
|
||
|
foreach (var lockId in unlockConditionTalent)
|
||
|
{
|
||
|
for (var j = 0; j < talent.Items.Count; j++)
|
||
|
{
|
||
|
if (talent.Items[j].Id != lockId) continue;
|
||
|
index = j;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (index > -1)
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return index;
|
||
|
}
|
||
|
}
|
||
|
}
|