notebook实装功能,galgame继续,一些bug修复
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
@@ -132,6 +132,10 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
private HashSet<string> _activeAttackBelowSkills = new HashSet<string>();
|
||||
private Dictionary<string, AppliedEffect> _appliedAttackBelowEffects = new Dictionary<string, AppliedEffect>();
|
||||
|
||||
// Track skills that are currently active because Attack is above a threshold (OnAttackAboveValue)
|
||||
private HashSet<string> _activeAttackAboveSkills = new HashSet<string>();
|
||||
private Dictionary<string, AppliedEffect> _appliedAttackAboveEffects = new Dictionary<string, AppliedEffect>();
|
||||
|
||||
// Prevent re-entrant mana-trigger loops, e.g. OnManaGained -> (skill adds mana) -> OnManaGained -> ...
|
||||
private bool _isTriggeringManaGained = false;
|
||||
private bool _isTriggeringManaLost = false;
|
||||
@@ -179,6 +183,8 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
if (_appliedAttackZeroEffects == null) _appliedAttackZeroEffects = new Dictionary<string, AppliedEffect>();
|
||||
if (_activeAttackBelowSkills == null) _activeAttackBelowSkills = new HashSet<string>();
|
||||
if (_appliedAttackBelowEffects == null) _appliedAttackBelowEffects = new Dictionary<string, AppliedEffect>();
|
||||
if (_activeAttackAboveSkills == null) _activeAttackAboveSkills = new HashSet<string>();
|
||||
if (_appliedAttackAboveEffects == null) _appliedAttackAboveEffects = new Dictionary<string, AppliedEffect>();
|
||||
}
|
||||
|
||||
private void LogVerbose(string message)
|
||||
@@ -304,6 +310,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
EvaluateHPPercentageTriggers(currentHP, true);
|
||||
EvaluateAttackZeroTriggers(true);
|
||||
EvaluateAttackBelowTriggers(true);
|
||||
EvaluateAttackAboveTriggers(true);
|
||||
}
|
||||
|
||||
// Allow editing in inspector to immediately reflect on UI (editor only)
|
||||
@@ -326,6 +333,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
EvaluateHPPercentageTriggers(currentHP);
|
||||
EvaluateAttackZeroTriggers();
|
||||
EvaluateAttackBelowTriggers();
|
||||
EvaluateAttackAboveTriggers();
|
||||
TryCastOnFullMana();
|
||||
}
|
||||
}
|
||||
@@ -1263,10 +1271,14 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
{
|
||||
if (isDead) return; // dead characters cannot change mana
|
||||
int old = currentMana;
|
||||
int requestedMana = mana;
|
||||
bool incomingOverflowGainAtFull = mana > maxMana && old >= maxMana;
|
||||
currentMana = Mathf.Clamp(mana, 0, maxMana);
|
||||
UpdateManaVisuals(old, animateFade, flashVisuals);
|
||||
// Only auto-cast when we actually reach full mana from below.
|
||||
if (old < maxMana && currentMana >= maxMana)
|
||||
// Also handle positive mana input while already full (e.g. maxMana=1 and receiving mana from adjacent skills):
|
||||
// without this, mana can stay at 100% with no further "reach full from below" transition.
|
||||
if ((old < maxMana && currentMana >= maxMana) || incomingOverflowGainAtFull)
|
||||
{
|
||||
TryCastOnFullMana();
|
||||
}
|
||||
@@ -1308,6 +1320,44 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
_isTriggeringManaLost = false;
|
||||
}
|
||||
}
|
||||
|
||||
int finalMana = currentMana;
|
||||
if (requestedMana != old || incomingOverflowGainAtFull)
|
||||
{
|
||||
string manaEventType;
|
||||
if (incomingOverflowGainAtFull && finalMana == old && old >= maxMana)
|
||||
{
|
||||
manaEventType = "ManaOverflowAtFull";
|
||||
GameplaySkillLogger.RecordConflictHint(
|
||||
"AllyMana",
|
||||
"OverflowGainAtFull",
|
||||
"ally=" + allyName + " | slot=" + slotIndex + " | requested=" + requestedMana + " | current=" + finalMana + " | max=" + maxMana);
|
||||
}
|
||||
else if (finalMana > old)
|
||||
{
|
||||
manaEventType = "ManaGain";
|
||||
}
|
||||
else if (finalMana < old)
|
||||
{
|
||||
manaEventType = "ManaSpend";
|
||||
}
|
||||
else
|
||||
{
|
||||
manaEventType = "ManaClampNoDelta";
|
||||
}
|
||||
|
||||
GameplaySkillLogger.RecordAllyResourceEvent(
|
||||
allyName,
|
||||
slotIndex,
|
||||
"Mana",
|
||||
manaEventType,
|
||||
requestedMana - old,
|
||||
finalMana - old,
|
||||
old,
|
||||
finalMana,
|
||||
maxMana,
|
||||
"SetCurrentMana");
|
||||
}
|
||||
}
|
||||
|
||||
public void ModifyMana(int delta, bool animateFade = true, bool flashVisuals = true)
|
||||
@@ -1398,6 +1448,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
{
|
||||
EvaluateAttackZeroTriggers();
|
||||
EvaluateAttackBelowTriggers();
|
||||
EvaluateAttackAboveTriggers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1597,6 +1648,18 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
ModifyHP(-delta, true, true);
|
||||
|
||||
int actual = oldHP - currentHP;
|
||||
GameplaySkillLogger.RecordAllyResourceEvent(
|
||||
allyName,
|
||||
slotIndex,
|
||||
"HP",
|
||||
"DamageTaken",
|
||||
amount,
|
||||
effective,
|
||||
oldHP,
|
||||
currentHP,
|
||||
maxHP,
|
||||
source != null ? source.name : "None");
|
||||
|
||||
if (actual != 0)
|
||||
{
|
||||
if (deferPopup)
|
||||
@@ -1618,6 +1681,18 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
int delta = Mathf.CeilToInt(amount * mult);
|
||||
ModifyHP(delta, true, true);
|
||||
int actual = currentHP - oldHP;
|
||||
GameplaySkillLogger.RecordAllyResourceEvent(
|
||||
allyName,
|
||||
slotIndex,
|
||||
"HP",
|
||||
"HealTaken",
|
||||
amount,
|
||||
amount * mult,
|
||||
oldHP,
|
||||
currentHP,
|
||||
maxHP,
|
||||
source != null ? source.name : "None");
|
||||
|
||||
if (actual != 0)
|
||||
{
|
||||
if (deferPopup)
|
||||
@@ -1936,6 +2011,7 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
vars["currentScore"] = currentScore;
|
||||
vars["idolScore"] = currentScore;
|
||||
vars["currentHP"] = currentHP;
|
||||
vars["noteBaseScore"] = bmm != null ? Mathf.Max(0, bmm.perNoteScore) : Mathf.Max(0, baseTrackScore);
|
||||
return vars;
|
||||
}
|
||||
// New: Evaluate HP-percent triggers and handle enter/leave transitions
|
||||
@@ -2336,6 +2412,131 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
}
|
||||
}
|
||||
|
||||
private void EvaluateAttackAboveTriggers(bool skipVFX = false)
|
||||
{
|
||||
if (!Application.isPlaying) return;
|
||||
if (SkillBuilder.Instance == null) return;
|
||||
|
||||
var so = SkillBuilder.Instance.GetAllyHeroSOBySlot(slotIndex);
|
||||
if (so == null) return;
|
||||
|
||||
int currAttack = attack;
|
||||
|
||||
var defs = _tmpSkillDefs;
|
||||
defs.Clear();
|
||||
if (so.equippedSkillGroupIDs != null && so.equippedSkillGroupIDs.Length > 0 && so.skillGroups != null)
|
||||
{
|
||||
foreach (var gid in so.equippedSkillGroupIDs)
|
||||
{
|
||||
if (gid == 0) continue;
|
||||
SkillGroup group = null;
|
||||
for (int k = 0; k < so.skillGroups.Length; k++)
|
||||
{
|
||||
var g = so.skillGroups[k];
|
||||
if (g != null && g.skillGroupID == gid) { group = g; break; }
|
||||
}
|
||||
if (group == null || group.skills == null) continue;
|
||||
for (int i = 0; i < group.skills.Length; i++)
|
||||
{
|
||||
var def = group.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fallbackGroup = so.GetPrimarySkillGroup();
|
||||
if (fallbackGroup != null && fallbackGroup.skills != null)
|
||||
{
|
||||
for (int i = 0; i < fallbackGroup.skills.Length; i++)
|
||||
{
|
||||
var def = fallbackGroup.skills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
else if (so.availableSkills != null)
|
||||
{
|
||||
for (int i = 0; i < so.availableSkills.Length; i++)
|
||||
{
|
||||
var def = so.availableSkills[i];
|
||||
if (def != null) defs.Add(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the closest satisfied threshold from below (largest attackTriggerValue such that attack > value).
|
||||
string selected = null;
|
||||
int selectedThreshold = int.MinValue;
|
||||
for (int i = 0; i < defs.Count; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
if (def == null) continue;
|
||||
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnAttackAboveValue) continue;
|
||||
|
||||
int thr = def.attackTriggerValue;
|
||||
if (thr < 0) thr = 0;
|
||||
|
||||
if (currAttack > thr && thr >= selectedThreshold)
|
||||
{
|
||||
selectedThreshold = thr;
|
||||
selected = def.skillId;
|
||||
}
|
||||
}
|
||||
|
||||
var vars = BuildFormulaVars(so);
|
||||
|
||||
for (int i = 0; i < defs.Count; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
if (def == null) continue;
|
||||
if (def.triggerCondition != SkillDefinition.SkillTrigger.OnAttackAboveValue) continue;
|
||||
|
||||
int thr = def.attackTriggerValue;
|
||||
if (thr < 0) thr = 0;
|
||||
|
||||
bool wasActive = _activeAttackAboveSkills.Contains(def.skillId);
|
||||
bool nowActive = (selected != null && def.skillId == selected) && (currAttack > thr);
|
||||
|
||||
float amount = 0f;
|
||||
if (!string.IsNullOrWhiteSpace(def.formula))
|
||||
{
|
||||
if (SkillDefinition.TryEvaluateFormula(def.formula, vars, out float fresult))
|
||||
amount = fresult;
|
||||
}
|
||||
|
||||
bool isReversible = IsEffectReversible(def.effectType);
|
||||
if (!isReversible)
|
||||
{
|
||||
if (!wasActive && nowActive)
|
||||
{
|
||||
_activeAttackAboveSkills.Add(def.skillId);
|
||||
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null, 1f, skipVFX);
|
||||
}
|
||||
else if (wasActive && !nowActive)
|
||||
{
|
||||
_activeAttackAboveSkills.Remove(def.skillId);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!wasActive && nowActive)
|
||||
{
|
||||
_activeAttackAboveSkills.Add(def.skillId);
|
||||
EffectSystem.Instance.ApplyEffect(def.defaultSelector, def.effectType, amount, 0f, this.gameObject, null, 1f, skipVFX);
|
||||
_appliedAttackAboveEffects[def.skillId] = new AppliedEffect(def.effectType, amount);
|
||||
}
|
||||
else if (wasActive && !nowActive)
|
||||
{
|
||||
_activeAttackAboveSkills.Remove(def.skillId);
|
||||
if (_appliedAttackAboveEffects.TryGetValue(def.skillId, out var applied))
|
||||
{
|
||||
ApplyInverseEffect(def.defaultSelector, applied.effectType, applied.amount);
|
||||
_appliedAttackAboveEffects.Remove(def.skillId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ActivateNextDamageRedirect(AllyCombatant redirector, float duration = 0f)
|
||||
{
|
||||
if (redirector == null) return;
|
||||
@@ -2370,12 +2571,6 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
state.expireTime = expireTime;
|
||||
state.activationOrder = ++s_nextDamageRedirectOrderSeed;
|
||||
state.iconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(redirector, PlayerBudeffIconType.dmg_redirect_toSelf, 0f, duration);
|
||||
|
||||
// If adjacent allies already have buffs, transfer one now and consume this one-shot redirect.
|
||||
if (redirector.TryTransferExistingBuffFromAdjacentToSelf())
|
||||
{
|
||||
ConsumeGlobalRedirect(redirector);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CleanupGlobalRedirects()
|
||||
@@ -3040,13 +3235,6 @@ public class AllyCombatant : MonoBehaviour, ICombatant
|
||||
_selfDamageRedirectToAdjacentIconId = null;
|
||||
}
|
||||
_selfDamageRedirectToAdjacentIconId = iBudeffPrefabController.Instance?.RegisterTimedEffect(this, PlayerBudeffIconType.dmg_redirect_toAdjacent, 0f, duration);
|
||||
|
||||
// "都给你!" rule: transfer to adjacent ally with the MOST buffs.
|
||||
var adj = FindAdjacentAllyByBuffCount(pickMost: true, requireAtLeastOneBuff: false);
|
||||
if (adj != null && TransferOneExistingBuffTo(adj))
|
||||
{
|
||||
ConsumeSelfDamageRedirectToAdjacent();
|
||||
}
|
||||
}
|
||||
|
||||
public void ActivateNonMissToPerfectRewrite(float duration)
|
||||
|
||||
Reference in New Issue
Block a user