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

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;
}
}
}