notebook实装功能,galgame继续,一些bug修复

This commit is contained in:
2026-03-06 02:08:43 +08:00
parent 9011fa022e
commit 1f4077cf21
468 changed files with 50175 additions and 11402 deletions
+203 -15
View File
@@ -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)