961 lines
36 KiB
C#
961 lines
36 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using asap.core;
|
||
using cfg;
|
||
using game;
|
||
using GameCore;
|
||
using Newtonsoft.Json.Linq;
|
||
using UI.Wheel;
|
||
using UnityEngine;
|
||
using Random = UnityEngine.Random;
|
||
|
||
namespace EventBossFight
|
||
{
|
||
public class EventBossFightManager:AEventDataManager<EventBossFightData>
|
||
{
|
||
protected override int EventType => 4;
|
||
protected override int EventSubType => 12;
|
||
protected override void RefreshData(int eventId)
|
||
{
|
||
var fishingEvent = Tables.TbFishingEvent.GetOrDefault(eventId);
|
||
if (fishingEvent == null) return;
|
||
var cacheData = GetCacheData();
|
||
if (cacheData != null && cacheData.EventID == eventId)
|
||
{
|
||
Data.SetData(cacheData);
|
||
TryResolveBossFightStageGroupIfNeeded();
|
||
SetRedPoint();
|
||
return;
|
||
}
|
||
|
||
InitData(eventId);
|
||
SetRedPoint();
|
||
}
|
||
|
||
protected override void InitData(int eventId)
|
||
{
|
||
base.InitData(eventId);
|
||
//ClearGuidance();
|
||
ResolveBossFightStageGroupAtActivityStart();
|
||
Data.BossFightPaidReviveCount = 0;
|
||
Data.BossFightAwaitingPaidRevive = false;
|
||
InitStageData();
|
||
Data.PendingFightBootstrap = false;
|
||
var welcomeGift = GContext.container.Resolve<FishingEventData>().GetInitWelcomeGift(eventId);
|
||
AddToken(welcomeGift);
|
||
SaveData();
|
||
}
|
||
|
||
#region Rewards
|
||
|
||
public int GetFinalBossRewardsDropID()
|
||
{
|
||
var lastStage = GetLastStage();
|
||
if (lastStage != null) return lastStage.DropId;
|
||
Debug.LogError($"[EventBossFightManager]GetFinalBossRewards: lastStage is null");
|
||
return 0;
|
||
|
||
}
|
||
|
||
public List<ItemData> GetFinalBossRewards()
|
||
{
|
||
var dropId= GetFinalBossRewardsDropID();
|
||
return GContext.container.Resolve<PlayerItemData>()
|
||
.GetItemDataByDropIdAndTypeLureInflation(Data.InflationRate, dropId);
|
||
}
|
||
public List<ItemData> GetBossRewards()
|
||
{
|
||
return GContext.container.Resolve<PlayerItemData>()
|
||
.GetItemDataByDropIdAndTypeLureInflation(Data.InflationRate, Data.StageConfig.DropId);
|
||
}
|
||
public string GetSpinType(int spinPointID)
|
||
{
|
||
var spinPointConfig = Tables.TbEventBossFightSpinPoints.GetOrDefault(spinPointID);
|
||
if (spinPointConfig == null || Data == null) return string.Empty;
|
||
return spinPointConfig.SpinType;
|
||
}
|
||
public void GetDropItemInfo(int spinPointID, out string iconName, out string textNum)
|
||
{
|
||
iconName = textNum = string.Empty;
|
||
var spinPointConfig = Tables.TbEventBossFightSpinPoints.GetOrDefault(spinPointID);
|
||
|
||
if (spinPointConfig == null || Data == null) return;
|
||
|
||
iconName = spinPointConfig.Icon ?? string.Empty;
|
||
textNum =ConvertTools.GetNumberString (spinPointConfig.EffectValue * Data.Magnification);
|
||
}
|
||
/// <summary>
|
||
/// 没有禁止magnification set 所以从外面传参进来
|
||
/// </summary>
|
||
public void UseDropItem(int spinPointID,int magnification,out int convertTokenCount,out int addAmount)
|
||
{
|
||
addAmount = 0;
|
||
convertTokenCount = 0;
|
||
var spinPointConfig = Tables.TbEventBossFightSpinPoints.GetOrDefault(spinPointID);
|
||
switch (spinPointConfig.SpinType)
|
||
{
|
||
case nameof(EEventBossFightSpinType.Cure):
|
||
PlayerRevertHp(spinPointConfig,magnification,out convertTokenCount,out addAmount);
|
||
break;
|
||
case nameof(EEventBossFightSpinType.Shield):
|
||
PlayerAddShield(spinPointConfig,magnification,out convertTokenCount,out addAmount);
|
||
break;
|
||
case nameof(EEventBossFightSpinType.AttackTier1):
|
||
case nameof(EEventBossFightSpinType.AttackTier2):
|
||
PlayerAttack(spinPointConfig,magnification);
|
||
break;
|
||
}
|
||
SaveData();
|
||
}
|
||
#endregion
|
||
#region Wheel
|
||
|
||
private List<IWheelReward> _rewards = new List<IWheelReward>();
|
||
public List<IWheelReward> GetWheelRewards()
|
||
{
|
||
if (Data?.InitConfig == null) return null;
|
||
|
||
if (_rewards == null)
|
||
_rewards = new List<IWheelReward>();
|
||
else
|
||
_rewards.Clear();
|
||
|
||
foreach (var id in Data.InitConfig.SpinPointList)
|
||
{
|
||
var rewardConfig = Tables.TbEventBossFightSpinPoints.Get(id);
|
||
if (rewardConfig == null
|
||
|| _rewards.Find(x => x.Index == rewardConfig.ID) != null)
|
||
continue;
|
||
_rewards.Add(new EventBossFightWheelRewardAdapter(rewardConfig,Data.InflationRate));
|
||
|
||
}
|
||
CalculateWeights();
|
||
return _rewards;
|
||
}
|
||
|
||
// 缓存数据
|
||
private int _totalWeight = 0;
|
||
private readonly List<int> _weightRanges = new List<int>();
|
||
|
||
private void CalculateWeights()
|
||
{
|
||
_totalWeight = _rewards.Sum(r => r.Weight);
|
||
BuildWeightRanges();
|
||
}
|
||
private void BuildWeightRanges()
|
||
{
|
||
_weightRanges.Clear();
|
||
var currentWeight = 0;
|
||
|
||
foreach (var reward in _rewards)
|
||
{
|
||
currentWeight += reward.Weight;
|
||
_weightRanges.Add(currentWeight);
|
||
}
|
||
}
|
||
|
||
// 根据权重随机获取奖励
|
||
public int GetRandomRewardIndex()
|
||
{
|
||
if (_totalWeight <= 0)
|
||
{
|
||
Debug.LogError("总权重为0");
|
||
return 0;
|
||
}
|
||
|
||
var randomValue = Random.Range(0, _totalWeight);
|
||
|
||
for (int i = 0; i < _weightRanges.Count; i++)
|
||
{
|
||
if (randomValue < _weightRanges[i])
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
|
||
return _rewards.Count - 1;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Stage
|
||
|
||
/// <summary>本周期关卡 id 列表(体力分档后取自 <see cref="EventBossFightInit.StageGroupList"/> 或回退 <see cref="EventBossFightInit.StageList"/>)。</summary>
|
||
public IReadOnlyList<int> GetResolvedStageIds() => Data.GetResolvedStageIdList();
|
||
|
||
/// <summary>
|
||
/// 根据活动开始时玩家体力与 <see cref="EventBossFightInit.TargetGroupByHook"/>(升序门槛,如 0、100、200)
|
||
/// 取最大满足 <c>energy >= hook[i]</c> 的下标 i;无满足时返回 0。
|
||
/// </summary>
|
||
public static int ResolveStageGroupIndexByEnergy(int energy, IList<int> targetGroupByHook)
|
||
{
|
||
if (targetGroupByHook == null || targetGroupByHook.Count == 0)
|
||
return 0;
|
||
var best = -1;
|
||
for (var i = 0; i < targetGroupByHook.Count; i++)
|
||
{
|
||
if (energy >= targetGroupByHook[i])
|
||
best = i;
|
||
}
|
||
|
||
return best < 0 ? 0 : best;
|
||
}
|
||
|
||
void ResolveBossFightStageGroupAtActivityStart()
|
||
{
|
||
ApplyBossFightStageGroupResolution(saveAfter: false);
|
||
}
|
||
|
||
void TryResolveBossFightStageGroupIfNeeded()
|
||
{
|
||
if (Data.BossFightStageGroupLocked)
|
||
return;
|
||
ApplyBossFightStageGroupResolution(saveAfter: true);
|
||
}
|
||
|
||
void ApplyBossFightStageGroupResolution(bool saveAfter)
|
||
{
|
||
var init = Data.InitConfig;
|
||
if (init == null)
|
||
return;
|
||
|
||
var energy = (int)GContext.container.Resolve<PlayerData>().Energy;
|
||
|
||
if (init.StageGroupList == null || init.StageGroupList.Count == 0
|
||
|| init.TargetGroupByHook == null || init.TargetGroupByHook.Count == 0)
|
||
{
|
||
Data.BossFightStageGroupIndex = 0;
|
||
Data.BossFightHookEnergySnapshot = energy;
|
||
Data.BossFightStageGroupLocked = true;
|
||
if (saveAfter)
|
||
SaveData();
|
||
return;
|
||
}
|
||
|
||
var tier = ResolveStageGroupIndexByEnergy(energy, init.TargetGroupByHook);
|
||
tier = Mathf.Clamp(tier, 0, init.StageGroupList.Count - 1);
|
||
if (init.StageGroupList[tier] == null || init.StageGroupList[tier].Count == 0)
|
||
{
|
||
Debug.LogWarning($"[EventBossFight] StageGroupList[{tier}] 为空,尝试使用首个非空分组或回退 StageList");
|
||
tier = 0;
|
||
for (var g = 0; g < init.StageGroupList.Count; g++)
|
||
{
|
||
if (init.StageGroupList[g] != null && init.StageGroupList[g].Count > 0)
|
||
{
|
||
tier = g;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
Data.BossFightStageGroupIndex = tier;
|
||
Data.BossFightHookEnergySnapshot = energy;
|
||
Data.BossFightStageGroupLocked = true;
|
||
if (saveAfter)
|
||
SaveData();
|
||
}
|
||
|
||
private EventBossFightStage GetLastStage()
|
||
{
|
||
var ids = GetResolvedStageIds();
|
||
if (ids == null || ids.Count == 0)
|
||
{
|
||
Debug.LogError($"[EventBossFightManager]GetLastStage: resolved stage list empty");
|
||
return Tables.TbEventBossFightStage.GetOrDefault(0);
|
||
}
|
||
|
||
return Tables.TbEventBossFightStage.GetOrDefault(ids[ids.Count - 1]);
|
||
}
|
||
public EventBossFightStage GetPreviousStage()
|
||
{
|
||
var ids = GetResolvedStageIds();
|
||
if (ids == null || ids.Count == 0)
|
||
return Tables.TbEventBossFightStage.GetOrDefault(0);
|
||
var index =Data.StageCountFrom1==1?ids.Count-1: Data.StageCountFrom1 - 2;
|
||
var id=ids[index];
|
||
return Tables.TbEventBossFightStage.GetOrDefault(id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 引导关闭等需要强制把当前关血量拉回表初始值时使用(不判断 Pending 标记)。
|
||
/// </summary>
|
||
void BootstrapFightFullReset()
|
||
{
|
||
Data.PendingFightBootstrap = false;
|
||
Data.BossFightAwaitingPaidRevive = false;
|
||
Data.BossFightPaidReviveCount = 0;
|
||
InitStageData();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 主界面 <see cref="EventBossFightMainPanel.CheckStageState"/>:仅当需要「回满当前关」时执行(例如无 IAP 阵亡后已回第一关再进界面)。
|
||
/// </summary>
|
||
public void ApplyPendingFightBootstrapIfNeeded()
|
||
{
|
||
if (!Data.PendingFightBootstrap)
|
||
return;
|
||
BootstrapFightFullReset();
|
||
SaveData();
|
||
}
|
||
|
||
public async void StartNewStageFight()
|
||
{
|
||
try
|
||
{
|
||
Data.IsRest = false;
|
||
Data.PendingFightBootstrap = false;
|
||
if (!TryStartNewRound())
|
||
{
|
||
InitStageData();
|
||
SaveData();
|
||
}
|
||
UITypes.EventBossFightLoadingPanel.SetType(Data.InitConfig.LoadingPanel);
|
||
await UIManager.Instance.ShowUILoad(UITypes.EventBossFightLoadingPanel);
|
||
var reviewPanel = UIManager.Instance.GetContainedUI(UITypes.EventBossFightReviewPopupPanel);
|
||
if (reviewPanel != null)
|
||
_= reviewPanel.GetComponent<EventBossFightReviewPanel>().ClosePanel();
|
||
var mainPanel= UIManager.Instance.GetContainedUI(UITypes.EventBossFightPanel);
|
||
if (mainPanel)
|
||
mainPanel.GetComponent<EventBossFightMainPanel>().OnStart();
|
||
else
|
||
await UIManager.Instance.ShowUI(UITypes.EventBossFightPanel);
|
||
|
||
GContext.Publish(new EndTransition());
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError($"jsd StartFight {e.Message}");
|
||
}
|
||
}
|
||
|
||
public void RestFight()
|
||
{
|
||
Data.IsRest = true;
|
||
InitStageData();
|
||
SaveData();
|
||
EventBossFightActTransition.PublishSwitchToFishing(Data?.InitConfig);
|
||
}
|
||
|
||
public bool TryStartNewRound(bool isInitData=true)
|
||
{
|
||
var stageIds = GetResolvedStageIds();
|
||
if (stageIds == null || stageIds.Count == 0 || Data.StageCountFrom1 <= stageIds.Count) return false;
|
||
Data.StageCountFrom1 = 1;
|
||
Data.RoundCountFrom1++;
|
||
Data.BossFightPaidReviveCount = 0;
|
||
Data.BossFightAwaitingPaidRevive = false;
|
||
if(isInitData)
|
||
InitStageData();
|
||
SaveData();
|
||
return true;
|
||
}
|
||
|
||
private void ResetStageProgress()
|
||
{
|
||
Data.StageCountFrom1 = 1;
|
||
Data.PendingFightBootstrap = true;
|
||
Data.BossFightAwaitingPaidRevive = false;
|
||
SaveData();
|
||
}
|
||
|
||
private void InitStageData()
|
||
{
|
||
Data.PlayerRemainHp = Data.StageConfig.PlayerHP;
|
||
Data.BossRemainHp = Data.StageConfig.BossHP;
|
||
Data.IsShowTips = true;
|
||
BossAttackTimeRefresh();
|
||
}
|
||
public void CheckStageIsMax()
|
||
{
|
||
var stageIds = GetResolvedStageIds();
|
||
if (stageIds != null && Data.StageCountFrom1 > stageIds.Count)
|
||
TryStartNewRound();
|
||
}
|
||
public bool CheckStageBossStateNoRewardPanel()
|
||
{
|
||
if (Data.BossRemainHp != 0) return false;
|
||
Data.IsRest = true;
|
||
if (!TryStartNewRound(false))
|
||
{
|
||
InitStageData();
|
||
SaveData();
|
||
}
|
||
return true;
|
||
}
|
||
|
||
public void CheckStageBossState()
|
||
{
|
||
if(CheckStageBossStateNoRewardPanel())
|
||
ShowRewardPanel();
|
||
}
|
||
public void CheckStagePlayerState()
|
||
{
|
||
if (Data.PlayerRemainHp != 0) return;
|
||
ShowEndPanel();
|
||
}
|
||
#endregion
|
||
#region boss
|
||
|
||
public bool TryBossAttack(out bool isReduceShield,out bool isReducePlayerHp,bool isRemainOnceAttack,out int lastDamage)
|
||
{
|
||
lastDamage = 0;
|
||
isReduceShield = false;
|
||
isReducePlayerHp = false;
|
||
if (IsAwaitingPaidRevive())
|
||
return false;
|
||
if(Data.IsRest)return false;
|
||
var nowSeconds = new DateTimeOffset(ZZTimeHelper.UtcNow()).ToUnixTimeSeconds();
|
||
if (nowSeconds < Data.BossAttackTime) return false;
|
||
var count=(nowSeconds-Data.BossAttackTime)/Data.StageConfig.AttackInterval+1;
|
||
//这些是离线挂机回来的逻辑
|
||
if (isRemainOnceAttack && count > 0)
|
||
{
|
||
int backupHp = Data.PlayerRemainHp;
|
||
int backupShield = Data.PlayerShieldCount;
|
||
Data.PlayerHpBeforeLastAttack = backupHp;
|
||
for (int i = 0; i < count; i++)
|
||
{
|
||
Debug.Log($"SimulateBossAttack start {i} backupHp {backupHp} backupShield {backupShield}");
|
||
if (SimulateBossAttack(backupHp, backupShield, out int newHp, out int newShield, out int damage))
|
||
{
|
||
Data.LastAttackDamage = damage;
|
||
Debug.Log($"SimulateBossAttack start {i} backupHp {backupHp} backupShield {backupShield} FatalAttackDamage {damage}");
|
||
Data.HasPendingLastAttack = true;
|
||
Data.PlayerRemainHp = 0;
|
||
Data.PlayerShieldCount=0;
|
||
SaveData();
|
||
return true;
|
||
}
|
||
|
||
backupHp = newHp;
|
||
backupShield = newShield;
|
||
Data.PlayerHpBeforeLastAttack = backupHp;
|
||
if(i == count-1)
|
||
{
|
||
Data.LastAttackDamage = damage;
|
||
if(damage==0)
|
||
Data.PlayerShieldBeforeLastAttack = backupShield+1;
|
||
Debug.Log($"SimulateBossAttack last attack start {i} backupHp {backupHp} backupShield {backupShield} FatalAttackDamage {damage}");
|
||
Data.HasPendingLastAttack = true;
|
||
Data.PlayerRemainHp = backupHp;
|
||
Data.PlayerShieldCount = backupShield;
|
||
SaveData();
|
||
return true;
|
||
}
|
||
Debug.Log($"SimulateBossAttack end {i} backupHp {backupHp} backupShield {backupShield}");
|
||
}
|
||
}
|
||
|
||
//这里是在线逻辑,基本上就是1次
|
||
for (var i = 0; i < count; i++)
|
||
BossAttack(ref isReduceShield,ref isReducePlayerHp,!isRemainOnceAttack,out lastDamage);
|
||
SaveData();
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行待播放的致命攻击,返回是否减盾和是否减血
|
||
/// </summary>
|
||
public bool ExecutePendingLastAttack(out bool isReduceShield,out bool isReducePlayerHp,out int damage)
|
||
{
|
||
damage = 0;
|
||
isReduceShield = isReducePlayerHp = false;
|
||
if (!Data.HasPendingLastAttack )
|
||
return false;
|
||
// 标记已处理
|
||
if (Data.LastAttackDamage == 0)
|
||
isReduceShield = true;
|
||
else
|
||
isReducePlayerHp = true;
|
||
Data.PlayerHpBeforeLastAttack = 0;
|
||
Data.PlayerShieldBeforeLastAttack = 0;
|
||
Data.HasPendingLastAttack = false;
|
||
damage = Data.LastAttackDamage;
|
||
Data.PlayerRemainHp -= Data.LastAttackDamage;
|
||
Data.LastAttackDamage = 0;
|
||
CheckPlayIsDead();
|
||
BossAttackTimeRefresh();
|
||
SaveData();
|
||
return true;
|
||
}
|
||
/// <summary>
|
||
/// 正常情况下每次应该只执行一次
|
||
/// </summary>
|
||
/// <param name="isReduceShield"></param>
|
||
/// <param name="isReducePlayerHp"></param>
|
||
/// <param name="isRefreshTime"></param>
|
||
private void BossAttack(ref bool isReduceShield,ref bool isReducePlayerHp,bool isRefreshTime,out int damage)
|
||
{
|
||
damage=0;
|
||
if (Data.PlayerShieldCount > 0)
|
||
{
|
||
Data.PlayerShieldCount --;
|
||
if(!isReduceShield)
|
||
isReduceShield = true;
|
||
}
|
||
else
|
||
{
|
||
damage = GetBossAttackDamage(Data.StageConfig);
|
||
Data.PlayerRemainHp -= damage;
|
||
if(!isReducePlayerHp)
|
||
isReducePlayerHp = true;
|
||
}
|
||
if(isRefreshTime)
|
||
BossAttackTimeRefresh();
|
||
|
||
var isDefeat= CheckPlayIsDead();
|
||
Agg_event_bossfight_attacked(Data.StageCountFrom1,Data.RoundCountFrom1,damage,isDefeat,isReduceShield);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 模拟一次boss攻击,返回是否致命和伤害值
|
||
/// </summary>
|
||
private bool SimulateBossAttack(int currentHp, int currentShield, out int newHp, out int newShield, out int damage)
|
||
{
|
||
newShield = currentShield;
|
||
newHp = currentHp;
|
||
damage = 0;
|
||
bool isBlcok = false;
|
||
if (newShield > 0)
|
||
{
|
||
newShield--;
|
||
isBlcok = true;
|
||
}
|
||
else
|
||
{
|
||
damage = GetBossAttackDamage(Data.StageConfig);
|
||
newHp -= damage;
|
||
}
|
||
var isDefeat = newHp <= 0;
|
||
|
||
Agg_event_bossfight_attacked(Data.StageCountFrom1,Data.RoundCountFrom1,damage,isDefeat,isBlcok);
|
||
return isDefeat;
|
||
}
|
||
private void BossAttackTimeRefresh()
|
||
{
|
||
var now = ZZTimeHelper.UtcNow();
|
||
Data.BossAttackTime = Data.StageConfig.AttackInterval + new DateTimeOffset(now).ToUnixTimeSeconds();
|
||
}
|
||
|
||
private int GetBossAttackDamage(EventBossFightStage config)
|
||
{
|
||
var list= config.BaseDamageList.Select(x => Tables.TbEventBossFightDamage.GetOrDefault(x).Weight);
|
||
var index= FtUtils.GetRandomIdxFromWeightList(list);
|
||
var id= config.BaseDamageList[index];
|
||
return Tables.TbEventBossFightDamage.GetOrDefault(id)?.BaseAttack*config.AttackMultiplier ?? 0;
|
||
}
|
||
|
||
public (int minDamage,int maxDamage) GetBossAttackRange(EventBossFightStage config)
|
||
{
|
||
if (config == null) return default;
|
||
if(config.BaseDamageList==null || config.BaseDamageList.Count==0) return default;
|
||
int min = Int32.MaxValue;
|
||
int max = Int32.MinValue;
|
||
foreach (var baseDamage in config.BaseDamageList)
|
||
{
|
||
var damageConfig = Tables.TbEventBossFightDamage.GetOrDefault(baseDamage);
|
||
if (damageConfig == null) continue;
|
||
if(damageConfig.BaseAttack>max)
|
||
max = damageConfig.BaseAttack;
|
||
if(damageConfig.BaseAttack<min)
|
||
min = damageConfig.BaseAttack;
|
||
}
|
||
|
||
min *= config.AttackMultiplier;
|
||
max *= config.AttackMultiplier;
|
||
return (min, max);
|
||
}
|
||
|
||
private void BossReduceHp(int count)
|
||
{
|
||
Data.BossRemainHp -= count;
|
||
CheckBossIsDead();
|
||
}
|
||
private void CheckBossIsDead()
|
||
{
|
||
try
|
||
{
|
||
if (Data.BossRemainHp > 0) return;
|
||
Agg_event_bossfight_stage(Data.StageCountFrom1, Data.RoundCountFrom1);
|
||
Data.BossRemainHp = 0;
|
||
var change=new ChangeSource(Data.EventID, "Event_Minigame1", "Event_Minigame1_Bossfight", null);
|
||
GContext.container.Resolve<PlayerItemData>()
|
||
.AddItemByDropLureInflation(Data.InflationRate, Data.StageConfig.DropId,change,false);
|
||
Data.StageCountFrom1++;
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region player
|
||
private void PlayerAddShield(EventBossFightSpinPoints spinConfig, int magnification,out int convertTokenCount,out int addAmount)
|
||
{
|
||
addAmount = 0;
|
||
convertTokenCount=0;
|
||
if(spinConfig?.SpinType!=nameof(EEventBossFightSpinType.Shield)) return;
|
||
Data.PlayerShieldCount += addAmount= spinConfig.EffectValue * magnification;
|
||
var overflowCount = Data.PlayerShieldCount - Data.InitConfig.ShieldCapacity;
|
||
if (overflowCount <= 0) return;
|
||
TryConvertToken(spinConfig, overflowCount,out convertTokenCount);
|
||
Data.PlayerShieldCount = Data.InitConfig.ShieldCapacity;
|
||
}
|
||
|
||
private void PlayerRevertHp(EventBossFightSpinPoints spinConfig, int magnification,out int convertTokenCount,out int addAmount)
|
||
{
|
||
addAmount = 0;
|
||
convertTokenCount = 0;
|
||
if(spinConfig?.SpinType!=nameof(EEventBossFightSpinType.Cure)) return;
|
||
Data.PlayerRemainHp += addAmount= spinConfig.EffectValue * magnification;
|
||
var overflowHp = Data.PlayerRemainHp - Data.StageConfig.PlayerHP;
|
||
if (overflowHp <= 0) return;
|
||
TryConvertToken(spinConfig, overflowHp,out convertTokenCount);
|
||
Data.PlayerRemainHp = Data.StageConfig.PlayerHP;
|
||
}
|
||
private void TryConvertToken(EventBossFightSpinPoints spinConfig, int overflowCount,out int convertTokenCount)
|
||
{
|
||
convertTokenCount = 0;
|
||
convertTokenCount = overflowCount / spinConfig.OverflowConvertRate;
|
||
AddToken(convertTokenCount);
|
||
}
|
||
private void PlayerAttack(EventBossFightSpinPoints spinConfig, int magnification)
|
||
{
|
||
if(spinConfig==null) return;
|
||
if(magnification<=0)return;
|
||
if(!IsAttackTypeSpin(spinConfig.SpinType)) return;
|
||
var damage = spinConfig.EffectValue * magnification;
|
||
BossReduceHp(damage);
|
||
}
|
||
|
||
public bool CheckPlayIsDead()
|
||
{
|
||
if (Data.PlayerRemainHp > 0) return false;
|
||
Data.PlayerRemainHp = 0;
|
||
Data.HasPendingLastAttack = false;
|
||
Data.LastAttackDamage=0;
|
||
Data.PlayerHpBeforeLastAttack = 0;
|
||
Data.PlayerShieldBeforeLastAttack = 0;
|
||
if (Data.BossFightAwaitingPaidRevive)
|
||
return true;
|
||
if (TryEnterPaidRevivePendingState())
|
||
{
|
||
Data.BossFightAwaitingPaidRevive = true;
|
||
SaveData();
|
||
return true;
|
||
}
|
||
|
||
ResetStageProgress();
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 当前付费复活应使用的 TbIAPItemList 配置 id(来自 GlobalConfig.EventBossFightIapList,下标封顶为最后一项)。
|
||
/// </summary>
|
||
public int GetPaidReviveIapTableId()
|
||
{
|
||
var list = Tables.TbGlobalConfig.EventBossFightIapList;
|
||
if (list == null || list.Count == 0)
|
||
return 0;
|
||
var idx = Math.Min(Data.BossFightPaidReviveCount, list.Count - 1);
|
||
return list[idx];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 已阵亡、活动有效且配置了可用 IAP 时,等待玩家付费复活(不重置关卡进度)。
|
||
/// </summary>
|
||
public bool IsAwaitingPaidRevive()
|
||
{
|
||
if (Data == null || !Data.IsActive || Data.PlayerRemainHp > 0 || !Data.BossFightAwaitingPaidRevive)
|
||
return false;
|
||
var iapId = GetPaidReviveIapTableId();
|
||
if (iapId <= 0)
|
||
return false;
|
||
return Tables.TbIAPItemList?.GetOrDefault(iapId) != null;
|
||
}
|
||
|
||
bool TryEnterPaidRevivePendingState()
|
||
{
|
||
if (!Data.IsActive)
|
||
return false;
|
||
var iapId = GetPaidReviveIapTableId();
|
||
if (iapId <= 0)
|
||
return false;
|
||
if (Tables.TbIAPItemList?.GetOrDefault(iapId) == null)
|
||
{
|
||
Debug.LogError($"[EventBossFight] EventBossFightIapList id {iapId} not found in TbIAPItemList, fallback to fail reset.");
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 付费成功后:玩家满血(当前关 PlayerHP)、Boss 剩余血量继承不变、清除等待复活标记,刷新 Boss 攻击计时。
|
||
/// </summary>
|
||
void ApplyPaidRevive()
|
||
{
|
||
Data.BossFightPaidReviveCount++;
|
||
Data.BossFightAwaitingPaidRevive = false;
|
||
Data.PendingFightBootstrap = false;
|
||
Data.PlayerRemainHp = Data.StageConfig.PlayerHP;
|
||
var maxBoss = Data.StageConfig.BossHP;
|
||
if (Data.BossRemainHp > maxBoss)
|
||
Data.BossRemainHp = maxBoss;
|
||
BossAttackTimeRefresh();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发起付费复活(无额外掉落展示)。
|
||
/// </summary>
|
||
public async Task<bool> TryPurchasePaidReviveAsync()
|
||
{
|
||
if (!IsAwaitingPaidRevive())
|
||
return false;
|
||
|
||
var iapTableId = GetPaidReviveIapTableId();
|
||
GContext.container.Resolve<PlayerItemData>().ResolveIapId(iapTableId, out var iapItem, out _);
|
||
if (iapItem == null)
|
||
return false;
|
||
|
||
var emptyGift = new List<ItemData>();
|
||
var buyType = new ShopBuyTypeData
|
||
{
|
||
type = ShopBuyType.None,
|
||
ID = Data.EventID,
|
||
IsHide = true,
|
||
NoShow = true,
|
||
extraPurcheData = new JObject
|
||
{
|
||
["event_bossfight_paid_revive"] = true,
|
||
["bossfight_paid_revive_index"] = Data.BossFightPaidReviveCount
|
||
}
|
||
};
|
||
|
||
var ok = await GContext.container.Resolve<PlayerShopData>()
|
||
.OnBuy(0, buyType, iapItem, emptyGift);
|
||
if (ok)
|
||
{
|
||
ApplyPaidRevive();
|
||
SaveData();
|
||
}
|
||
|
||
return ok;
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region Pack
|
||
|
||
public async void ShowFestPack()
|
||
{
|
||
try
|
||
{
|
||
if (Data?.InitConfig == null) return;
|
||
var packData = new EventNeoNormalPackData
|
||
{
|
||
EventId = Data.EventID,
|
||
PackId = Data.InitConfig.PackId,
|
||
// Panel = Data.InitConfig.PackPanel
|
||
};
|
||
await EventNeoNormalPackPanel.TryShowAsync(packData);
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.LogError(e);
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
public void ShowReviewPanel()
|
||
{
|
||
UITypes.EventBossFightReviewPopupPanel.SetType(Data.InitConfig.ReviewPanel);
|
||
_= UIManager.Instance.ShowUILoad(UITypes.EventBossFightReviewPopupPanel);
|
||
}
|
||
private void ShowRewardPanel()
|
||
{
|
||
UITypes.EventBossFightRewardPopupPanel.SetType(Data.InitConfig.RewardPanel);
|
||
_= UIManager.Instance.ShowUILoad(UITypes.EventBossFightRewardPopupPanel);
|
||
}
|
||
private void ShowEndPanel()
|
||
{
|
||
UITypes.EventBossFightEndingPopupPanel.SetType(Data.InitConfig.EndingPanel);
|
||
_= UIManager.Instance.ShowUILoad(UITypes.EventBossFightEndingPopupPanel);
|
||
}
|
||
|
||
public void SetGuideState(bool isShow)
|
||
{
|
||
if(Data.IsShowGuide==isShow) return;
|
||
Data.IsShowGuide = isShow;
|
||
BootstrapFightFullReset();
|
||
SaveData();
|
||
}
|
||
public void SetShowTipsState(bool isShow)
|
||
{
|
||
if(Data.IsShowTips==isShow) return;
|
||
Data.IsShowTips = isShow;
|
||
SaveData();
|
||
}
|
||
#region Token
|
||
public string GetTokenName()
|
||
{
|
||
return Tables.TbItem.GetOrDefault(Data.InitConfig.ItemID)?.Icon??string.Empty;
|
||
}
|
||
public bool TryUseToken()
|
||
{
|
||
var currentAmount = GetTokenCount();
|
||
if (currentAmount < Data.Magnification*Data.InitConfig.SpinRequire)
|
||
{
|
||
ShowFestPack();
|
||
return false;
|
||
}
|
||
RemoveToken(Data.Magnification*Data.InitConfig.SpinRequire);
|
||
SetRedPoint();
|
||
return true;
|
||
}
|
||
public override void AddToken(int count)
|
||
{
|
||
base.AddToken(count);
|
||
SetRedPoint();
|
||
}
|
||
#endregion
|
||
private static bool IsAttackTypeSpin(string type)
|
||
{
|
||
return type.Equals(nameof(EEventBossFightSpinType.AttackTier1)) ||
|
||
type.Equals(nameof(EEventBossFightSpinType.AttackTier2));
|
||
}
|
||
#region Red
|
||
private string GetRedPointKey()
|
||
{
|
||
return "eventBossFight.red";
|
||
}
|
||
private bool GetRedPointState()
|
||
{
|
||
if (!Data.IsActive) return false;
|
||
return GetTokenCount() > 0;
|
||
}
|
||
private void SetRedPoint()
|
||
{
|
||
RedPointManager.Instance?.SetRedPointState(GetRedPointKey(), GetRedPointState(),GetTokenCount());
|
||
}
|
||
#endregion
|
||
|
||
#region guidance
|
||
|
||
// public void ClearGuidance()
|
||
// {
|
||
// GContext.container.Resolve<GuideDataCenter>().ClearGuide("BossFight01");
|
||
// }
|
||
//
|
||
// public void TryTriggerGuidance()
|
||
// {
|
||
// if (Data.PlayerRemainHp == 0)
|
||
// return;
|
||
// GContext.container.Resolve<GuideDataCenter>().TriggerGuide("BossFight01","EventBossFightUnderseaPanel",false);
|
||
// }
|
||
#endregion
|
||
#region Agg
|
||
|
||
/// <summary>
|
||
/// event_bossfight 转动一次上报
|
||
/// </summary>
|
||
/// <param name="stage_id">当前第几关</param>
|
||
/// <param name="round">当前第几轮</param>
|
||
/// <param name="multiple">倍率</param>
|
||
/// <param name="point_type">积分类型</param>
|
||
/// <param name="shield_count">玩家当前护盾</param>
|
||
/// <param name="player_hp">玩家当前生命值</param>
|
||
/// <param name="overflow_count">溢出转换</param>
|
||
/// <param name="boss_hp">boss剩余血量</param>
|
||
/// <param name="player_group">体力分档序号,<b>从 1 开始</b>(数据层 <see cref="EventBossFightData.BossFightStageGroupIndex"/> 为 0 起)</param>
|
||
public void Agg_event_bossfight(int stage_id,int round,int multiple,string point_type,int shield_count,int player_hp,int overflow_count,int boss_hp,int player_group)
|
||
{
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("event_bossfight"))
|
||
{
|
||
e.AddContent(nameof(stage_id), stage_id)
|
||
.AddContent(nameof(round), round)
|
||
.AddContent(nameof(multiple), multiple)
|
||
.AddContent(nameof(point_type), point_type)
|
||
.AddContent(nameof(shield_count), shield_count).
|
||
AddContent(nameof(player_hp), player_hp)
|
||
.AddContent(nameof(overflow_count), overflow_count)
|
||
.AddContent(nameof(boss_hp), boss_hp)
|
||
.AddContent(nameof(player_group), player_group);
|
||
}
|
||
#endif
|
||
}
|
||
/// <summary>
|
||
/// event_bossfight_stage 击败boss时上报
|
||
/// </summary>
|
||
/// <param name="stage_id">当前第几关</param>
|
||
/// <param name="round">当前第几轮</param>
|
||
public void Agg_event_bossfight_stage (int stage_id,int round)
|
||
{
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("event_bossfight_stage"))
|
||
{
|
||
e.AddContent(nameof(stage_id), stage_id)
|
||
.AddContent(nameof(round), round);
|
||
}
|
||
#endif
|
||
}
|
||
/// <summary>
|
||
/// event_bossfight_attacked 被boss攻击时上报
|
||
/// </summary>
|
||
/// <param name="stage_id">当前第几关</param>
|
||
/// <param name="round">当前第几轮</param>
|
||
/// <param name="attack">boss攻击伤害</param>
|
||
/// <param name="is_defeat">是否被击败</param>
|
||
/// <param name="is_block">是否被格挡</param>
|
||
public void Agg_event_bossfight_attacked (int stage_id,int round,int attack,bool is_defeat,bool is_block)
|
||
{
|
||
#if AGG
|
||
using (var e = GEvent.GameEvent("event_bossfight_attacked"))
|
||
{
|
||
e.AddContent(nameof(stage_id), stage_id)
|
||
.AddContent(nameof(round), round)
|
||
.AddContent(nameof(attack), attack)
|
||
.AddContent(nameof(is_defeat), is_defeat)
|
||
.AddContent(nameof(is_block), is_block);
|
||
}
|
||
#endif
|
||
}
|
||
#endregion
|
||
#if UNITY_EDITOR
|
||
|
||
#region DebugFuc
|
||
public void Debug_StartTargetStageFight(int stageIndex)
|
||
{
|
||
Data.StageCountFrom1 = stageIndex;
|
||
InitStageData();
|
||
SaveData();
|
||
}
|
||
public void Debug_UseDropItem(int spinPointID,int magnification,out int convertTokenCount,out int addAmount)
|
||
{
|
||
addAmount = 0;
|
||
convertTokenCount = 0;
|
||
var spinPointConfig = Tables.TbEventBossFightSpinPoints.GetOrDefault(spinPointID);
|
||
switch (spinPointConfig.SpinType)
|
||
{
|
||
case nameof(EEventBossFightSpinType.Cure):
|
||
PlayerRevertHp(spinPointConfig,magnification,out convertTokenCount,out addAmount);
|
||
break;
|
||
case nameof(EEventBossFightSpinType.Shield):
|
||
PlayerAddShield(spinPointConfig,magnification,out convertTokenCount,out addAmount);
|
||
break;
|
||
case nameof(EEventBossFightSpinType.AttackTier1):
|
||
case nameof(EEventBossFightSpinType.AttackTier2):
|
||
PlayerAttack(spinPointConfig,magnification);
|
||
break;
|
||
}
|
||
SaveData();
|
||
}
|
||
#endregion
|
||
#endif
|
||
}
|
||
} |