1220 lines
51 KiB
C#
1220 lines
51 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Threading.Tasks;
|
||
using AibisDream.FixSystem;
|
||
using AibisDream;
|
||
using AibisDream.Framework;
|
||
using AibisDream.Utility;
|
||
using UnityEngine.Localization;
|
||
using UnityEngine.Localization.Settings;
|
||
using Yarn.Unity;
|
||
|
||
namespace AibisDream.MiniGame.Language
|
||
{
|
||
/// <summary>
|
||
/// 表达系统的 Yarn 命令接口(原语言系统)
|
||
/// </summary>
|
||
public static class LanguageYarnCommand
|
||
{
|
||
private static AibisDream.ExpressionManager ExpressionManager => FixSystemCenter.SystemDic.Get<AibisDream.ExpressionManager>();
|
||
|
||
private static Locale GetCommandLocale()
|
||
{
|
||
Locale current = LocalizationSettings.SelectedLocale;
|
||
return ExpressionManager != null
|
||
? ExpressionManager.GetExpressionLocale(current)
|
||
: current;
|
||
}
|
||
|
||
private static IEnumerator ResolveTextReferences(
|
||
Locale locale,
|
||
System.Action<string[]> completed,
|
||
params string[] references)
|
||
{
|
||
var tasks = new Task<string>[references.Length];
|
||
for (int i = 0; i < references.Length; i++)
|
||
tasks[i] = LocalizationKit.LocalizeParamAsync(references[i], locale);
|
||
|
||
Task<string[]> all = Task.WhenAll(tasks);
|
||
while (!all.IsCompleted)
|
||
yield return null;
|
||
|
||
if (all.Status != TaskStatus.RanToCompletion)
|
||
{
|
||
completed?.Invoke(System.Array.Empty<string>());
|
||
yield break;
|
||
}
|
||
|
||
string[] values = all.Result;
|
||
for (int i = 0; i < values.Length; i++)
|
||
{
|
||
if (LocalizationKit.IsMissingParamResult(references[i], values[i]))
|
||
values[i] = null;
|
||
}
|
||
completed?.Invoke(values);
|
||
}
|
||
|
||
private static bool ValidateRequiredText(string value, string command, string parameter)
|
||
{
|
||
if (!string.IsNullOrWhiteSpace(value))
|
||
return true;
|
||
|
||
UnityEngine.Debug.LogError(
|
||
$"[LanguageYarnCommand] {command} 的玩家可见参数 {parameter} 解析为空;命令已安全结束。");
|
||
return false;
|
||
}
|
||
|
||
public static bool TryParseExpressionTokens(string value, out List<string> tokens)
|
||
{
|
||
tokens = new List<string>();
|
||
if (string.IsNullOrWhiteSpace(value))
|
||
return false;
|
||
|
||
if (value.Contains("|"))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] 干扰 token 使用了全角分隔符“|”;请改用半角“|”。");
|
||
return false;
|
||
}
|
||
|
||
string[] parts = value.Split('|');
|
||
foreach (string part in parts)
|
||
{
|
||
string token = part.Trim();
|
||
if (!string.IsNullOrEmpty(token))
|
||
tokens.Add(token);
|
||
}
|
||
|
||
return tokens.Count > 0;
|
||
}
|
||
|
||
public static bool TryParseExpressionPool(
|
||
string value,
|
||
out List<string> units)
|
||
{
|
||
return TryParseExpressionPool(
|
||
value,
|
||
ExpressionParticleUnitMode.Grapheme,
|
||
out units);
|
||
}
|
||
|
||
public static bool TryParseExpressionPool(
|
||
string value,
|
||
ExpressionParticleUnitMode mode,
|
||
out List<string> units)
|
||
{
|
||
return ExpressionTextTokenizer.TryTokenizePool(value, mode, out units);
|
||
}
|
||
|
||
private static bool TryGetPresentation(out LogReleasePresentationController presentation)
|
||
{
|
||
presentation = ExpressionManager != null
|
||
? ExpressionManager.LogReleasePresentation
|
||
: null;
|
||
if (presentation != null)
|
||
return true;
|
||
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] LogReleasePresentationController 未找到;演出命令安全结束。");
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 启动表达粒子系统(在打开视图后调用),等粒子字入场完成即可返回(可交互);panel 动效并行不阻塞。
|
||
/// 流程:若有上一轮则先淡出字与连线 → 再淡入火山释放 log → 再启动新一轮并等粒子就绪。
|
||
/// 注意:Yarn Spinner 2.x 对同名 <see cref="YarnCommand"/> 重载支持不可靠,只保留一个注册入口,用可选参数区分 3/4 参调用。
|
||
/// <<start_expression "l10n.hs.exp.log1.tokens" "l10n.hs.exp.log1.target" "ExpressionCompleted">>
|
||
/// <<start_expression "l10n.hs.exp.log1.tokens" "l10n.hs.exp.log1.target" "ExpressionCompleted" 24>>
|
||
/// (第四项为「非目标」候选粒子总数,候选池 = 目标句字数 + 该值;不写第四项则用 Inspector 的 Candidate Count)
|
||
/// </summary>
|
||
/// <param name="anxietyPhrasesStr">干扰短句(笑话等),用 | 分隔,用于非目标粒子字符池,如:"哈哈|嘿嘿|呵呵"</param>
|
||
/// <param name="targetSentence">目标句子</param>
|
||
/// <param name="completionNode">完成时触发的对话节点(可空字符串)</param>
|
||
/// <param name="nonTargetParticleTotal">非目标候选粒子总数;<0 表示使用 Inspector 默认</param>
|
||
[YarnCommand("start_expression")]
|
||
public static IEnumerator StartExpression(
|
||
string anxietyPhrasesStr,
|
||
string targetSentence,
|
||
string completionNode = "",
|
||
float nonTargetParticleTotal = -1f)
|
||
{
|
||
return RunStartExpression(
|
||
anxietyPhrasesStr,
|
||
targetSentence,
|
||
completionNode ?? "",
|
||
UnityEngine.Mathf.RoundToInt(nonTargetParticleTotal));
|
||
}
|
||
|
||
private static IEnumerator RunStartExpression(string anxietyPhrasesStr, string targetSentence, string completionNode, int nonTargetParticleTotal)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!请确保场景中有 ExpressionManager 组件。");
|
||
yield break;
|
||
}
|
||
|
||
Locale locale = LocalizationSettings.SelectedLocale;
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
locale,
|
||
values => localized = values,
|
||
anxietyPhrasesStr,
|
||
targetSentence,
|
||
ConstRef.ExpressParticlePoolParam);
|
||
if (localized == null || localized.Length != 3 ||
|
||
!ValidateRequiredText(localized[0], "start_expression", nameof(anxietyPhrasesStr)) ||
|
||
!ValidateRequiredText(localized[1], "start_expression", nameof(targetSentence)) ||
|
||
!ValidateRequiredText(localized[2], "start_expression", "defaultCharacterPool"))
|
||
yield break;
|
||
|
||
if (!TryParseExpressionTokens(localized[0], out List<string> phrases))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] start_expression 没有有效的干扰 token;命令已安全结束。");
|
||
yield break;
|
||
}
|
||
ExpressionParticleLocaleSettings profile =
|
||
ExpressionManager.ParticleLanguageProfile != null
|
||
? ExpressionManager.ParticleLanguageProfile.Resolve(locale.Identifier)
|
||
: ExpressionParticleLocaleSettings.CreateFallback();
|
||
if (!ExpressionRoundTextSnapshot.TryCreate(
|
||
locale.Identifier,
|
||
profile,
|
||
localized[1],
|
||
phrases,
|
||
localized[2],
|
||
out ExpressionRoundTextSnapshot snapshot,
|
||
out string snapshotError))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
$"[LanguageYarnCommand] start_expression 粒子单位解析失败:{snapshotError}");
|
||
yield break;
|
||
}
|
||
|
||
// 先清理上一轮演出,再淡出旧文字/连线并恢复 Log 操作界面。
|
||
yield return ExpressionManager.PreparePresentationForNextRound();
|
||
yield return ExpressionManager.FadeOutParticlesBeforeNewRound(0f);
|
||
yield return ExpressionManager.EnsureReleaseLogFadedIn(1f);
|
||
ExpressionManager.SetActiveExpressionLocale(locale);
|
||
ExpressionManager.StartSystem(
|
||
snapshot,
|
||
completionNode,
|
||
nonTargetParticleTotal);
|
||
yield return ExpressionManager.WaitUntilExpressionFlowReady();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用 ExpressionContentCatalog 中的轮次配置启动表达系统。
|
||
/// <<start_expression_round "log1" "LOG1梳理完成">>
|
||
/// </summary>
|
||
[YarnCommand("start_expression_round")]
|
||
public static IEnumerator StartExpressionRound(string roundId, string completionNode = "")
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!请确保场景中有 ExpressionManager 组件。");
|
||
yield break;
|
||
}
|
||
|
||
ExpressionContentCatalog catalog = ExpressionManager.ContentCatalog;
|
||
if (catalog == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] ExpressionContentCatalog 未配置;无法启动表达轮次。");
|
||
yield break;
|
||
}
|
||
|
||
List<string> catalogErrors = catalog.GetValidationErrors();
|
||
if (catalogErrors.Count > 0)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] ExpressionContentCatalog 数据无效;无法启动表达轮次。\n" +
|
||
string.Join("\n", catalogErrors));
|
||
yield break;
|
||
}
|
||
|
||
if (!catalog.TryGetRound(roundId, out ExpressionRoundDefinition round))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
$"[LanguageYarnCommand] ExpressionContentCatalog 中不存在轮次 '{roundId}'。");
|
||
yield break;
|
||
}
|
||
|
||
Locale locale = LocalizationSettings.SelectedLocale;
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
locale,
|
||
values => localized = values,
|
||
round.TokenReference,
|
||
round.TargetReference,
|
||
ConstRef.ExpressParticlePoolParam);
|
||
if (localized == null || localized.Length != 3 ||
|
||
!ValidateRequiredText(localized[0], "start_expression_round", nameof(round.TokenReference)) ||
|
||
!ValidateRequiredText(localized[1], "start_expression_round", nameof(round.TargetReference)) ||
|
||
!ValidateRequiredText(localized[2], "start_expression_round", "defaultCharacterPool"))
|
||
yield break;
|
||
|
||
if (!TryParseExpressionTokens(localized[0], out List<string> phrases))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
$"[LanguageYarnCommand] 表达轮次 '{roundId}' 没有有效的干扰 token。");
|
||
yield break;
|
||
}
|
||
ExpressionParticleLocaleSettings profile =
|
||
ExpressionManager.ParticleLanguageProfile != null
|
||
? ExpressionManager.ParticleLanguageProfile.Resolve(locale.Identifier)
|
||
: ExpressionParticleLocaleSettings.CreateFallback();
|
||
if (!ExpressionRoundTextSnapshot.TryCreate(
|
||
locale.Identifier,
|
||
profile,
|
||
localized[1],
|
||
phrases,
|
||
localized[2],
|
||
out ExpressionRoundTextSnapshot snapshot,
|
||
out string snapshotError))
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
$"[LanguageYarnCommand] 表达轮次 '{roundId}' 粒子单位解析失败:{snapshotError}");
|
||
yield break;
|
||
}
|
||
|
||
yield return ExpressionManager.PreparePresentationForNextRound();
|
||
yield return ExpressionManager.FadeOutParticlesBeforeNewRound(0f);
|
||
yield return ExpressionManager.EnsureReleaseLogFadedIn(1f);
|
||
ExpressionManager.SetActiveExpressionLocale(locale);
|
||
ExpressionManager.StartSystem(
|
||
snapshot,
|
||
completionNode ?? string.Empty,
|
||
round.NonTargetParticleCount);
|
||
yield return ExpressionManager.WaitUntilExpressionFlowReady();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 关闭表达系统
|
||
/// <<close_expression>>
|
||
/// </summary>
|
||
[YarnCommand("close_expression")]
|
||
public static void CloseExpression()
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.CloseView();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 重置表达系统(使用当前配置)
|
||
/// <<reset_expression>>
|
||
/// 或
|
||
/// <<reset_expression false>> // 不播放入场动画
|
||
/// </summary>
|
||
/// <param name="playEntranceEffect">是否播放入场效果</param>
|
||
[YarnCommand("reset_expression")]
|
||
public static void ResetExpression(bool playEntranceEffect = true)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.ResetSystem(playEntranceEffect);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 触发聚焦稳定化(从文字飘动保持态 → 概率递增 → 文字锁定 → 排列),等待全流程完成后返回
|
||
/// <<resolve_expression_focus>>
|
||
/// </summary>
|
||
[YarnCommand("resolve_expression_focus")]
|
||
public static IEnumerator ResolveExpressionFocus()
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
yield break;
|
||
}
|
||
yield return ExpressionManager.StartCoroutine(ExpressionManager.ResolveFocusAndWait());
|
||
}
|
||
|
||
/// <summary>
|
||
/// 淡入 ExpressionManager 的 Sprite 图片
|
||
/// <<expression_sprite_fade_in>>
|
||
/// <<expression_sprite_fade_in 1.5>>
|
||
/// </summary>
|
||
/// <param name="duration">淡入时长(秒),默认 1</param>
|
||
[YarnCommand("expression_sprite_fade_in")]
|
||
public static IEnumerator ExpressionSpriteFadeIn(float duration = 1f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
yield break;
|
||
}
|
||
yield return ExpressionManager.StartCoroutine(ExpressionManager.FadeInSpriteOverlay(duration));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 淡出 ExpressionManager 的 Sprite 图片
|
||
/// <<expression_sprite_fade_out>>
|
||
/// <<expression_sprite_fade_out 1.5>>
|
||
/// </summary>
|
||
/// <param name="duration">淡出时长(秒),默认 1</param>
|
||
[YarnCommand("expression_sprite_fade_out")]
|
||
public static IEnumerator ExpressionSpriteFadeOut(float duration = 1f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
yield break;
|
||
}
|
||
yield return ExpressionManager.StartCoroutine(ExpressionManager.FadeOutSpriteOverlay(duration));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Glitch 噪波过渡,等待完成后再继续下一步。
|
||
/// <<glitch_transition 0.95 2>> -- 从 0 到 0.95,2 秒
|
||
/// <<glitch_transition 0.95>> -- 立即到 0.95(duration 默认 0)
|
||
/// <<glitch_transition 0.3 0.45 1>> -- 从 0.3 到 0.45,1 秒
|
||
/// </summary>
|
||
[YarnCommand("glitch_transition")]
|
||
public static IEnumerator GlitchTransition(float to, float duration = 0f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
yield break;
|
||
}
|
||
yield return ExpressionManager.StartCoroutine(ExpressionManager.TransitionGlitchAndWait(to, duration));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 记忆淡入(非阻塞):透明度在 fadeDuration 内显现,画面同时按 pushSpeed 持续推近;
|
||
/// targetOpacity 是本次淡入的目标透明度。horizontalSpeed 为旧 Yarn 兼容参数,基础层不再横移。
|
||
/// 进记忆前可用 glitch_transition 铺 noise;begin 时会把 noise 淡出,改由记忆材质
|
||
/// _SimGlitch / Analog 承接轻微失真。preserveGlitch 已忽略。争夺段 jolt/tear/impact 仍叠加。
|
||
/// </summary>
|
||
[YarnCommand("expression_memory_begin")]
|
||
public static void ExpressionMemoryBegin(
|
||
string memoryKey,
|
||
float fadeDuration = -1f,
|
||
float pushSpeed = -1f,
|
||
float horizontalSpeed = -1f,
|
||
bool preserveGlitch = false,
|
||
float targetOpacity = 1f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
return;
|
||
}
|
||
ExpressionManager.StartCoroutine(
|
||
ExpressionManager.BeginExpressionMemory(
|
||
memoryKey,
|
||
fadeDuration,
|
||
pushSpeed,
|
||
horizontalSpeed,
|
||
preserveGlitch,
|
||
targetOpacity));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 随对白把当前记忆透明度推进到目标值(非阻塞)。
|
||
/// <<expression_memory_alpha 0.65 0.45>>
|
||
/// </summary>
|
||
[YarnCommand("expression_memory_alpha")]
|
||
public static void ExpressionMemoryAlpha(float target, float duration = 0.4f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.SetActiveMemoryOpacity(target, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 显式淡入火山脸,并等待淡入完成。
|
||
/// <<expression_face_fade_in 0.45>>
|
||
/// </summary>
|
||
[YarnCommand("expression_face_fade_in")]
|
||
public static IEnumerator ExpressionFaceFadeIn(float duration = 0.45f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.BeginFaceEntry(duration));
|
||
}
|
||
|
||
// 以下命令是一动作一命令。非 IEnumerator 命令只启动效果,节拍由 Yarn 的 wait 控制。
|
||
|
||
[YarnCommand("expression_lie_prepare")]
|
||
public static void ExpressionLiePrepare(float freezeDuration, float noiseTarget)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PrepareLieVisuals(freezeDuration, noiseTarget);
|
||
}
|
||
|
||
[YarnCommand("expression_truth_squeeze")]
|
||
public static void ExpressionTruthSqueeze(float duration, float jitter, float alpha)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.SqueezeTruthCharacters(duration, jitter, alpha);
|
||
}
|
||
|
||
[YarnCommand("expression_lie_crack_begin")]
|
||
public static void ExpressionLieCrackBegin(float lieAlpha)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.BeginLieCracking(lieAlpha);
|
||
}
|
||
|
||
[YarnCommand("expression_screen_dim")]
|
||
public static void ExpressionScreenDim(float alpha, float riseDuration, float fallDuration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayScreenDim(alpha, riseDuration, fallDuration);
|
||
}
|
||
|
||
[YarnCommand("expression_memory_jolt")]
|
||
public static void ExpressionMemoryJolt(float offset, float duration, float tear)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayMemoryJolt(offset, duration, tear);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 调节记忆的轻微失真扰动(非阻塞)。默认强度由演出预设决定,本命令用于演出中微调。
|
||
/// <<expression_memory_sim 0.55 0.3>>
|
||
/// </summary>
|
||
[YarnCommand("expression_memory_sim")]
|
||
public static void ExpressionMemorySim(float target, float duration = 0.3f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.SetSimulationGlitch(target, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 真话聚合尝试(非阻塞):目标字被慢慢拉向最终位置并按字序逐个锁定成真心话。
|
||
/// target 小于 1 时最后几个字永远差一点锁不住——"感觉快要成功了"。
|
||
/// <<expression_truth_gather 0.9 3.2>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_gather")]
|
||
public static void ExpressionTruthGather(float target = 0.9f, float duration = 3f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.BeginTruthGatherAttempt(target, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 真话聚合被打断(非阻塞):目标字向外爆散并恢复乱跳。burst 为世界单位的爆散距离。
|
||
/// <<expression_truth_scatter 0.9>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_scatter")]
|
||
public static void ExpressionTruthScatter(float burst = 0.9f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.ScatterTruthGatherAttempt(burst);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 真话聚合挣扎(非阻塞):向 target 推进的途中反复"够到一点又被拽回去",
|
||
/// 已锁的字会崩开重新乱跳。挣扎感来自聚合曲线本身,不使用任何颜色或额外特效。
|
||
/// <<expression_truth_strain 0.86 3.2 3>>
|
||
/// </summary>
|
||
/// <param name="target">最终停留的聚合进度</param>
|
||
/// <param name="duration">整段时长</param>
|
||
/// <param name="tugs">回抽次数(0~4);<=0 时退化为 expression_truth_gather</param>
|
||
[YarnCommand("expression_truth_strain")]
|
||
public static void ExpressionTruthStrain(float target = 0.85f, float duration = 3f, float tugs = 2f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
{
|
||
presentation.BeginTruthStrainAttempt(
|
||
target,
|
||
duration,
|
||
UnityEngine.Mathf.RoundToInt(tugs));
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 真话聚合崩塌(非阻塞):进度被一口气拽回 0,字沿原路退回悬浮位并恢复乱跳。
|
||
/// 与 expression_truth_scatter 的区别:scatter 是径向爆散,collapse 是"被拽回去"。
|
||
/// <<expression_truth_collapse 0.45>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_collapse")]
|
||
public static void ExpressionTruthCollapse(float duration = 0.45f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.CollapseTruthGatherAttempt(duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 火山脸错误闪红(非阻塞):聚合失败时短暂替换红屏 Sprite,随后恢复,并轻微下沉。
|
||
/// 注意:LOG1/LOG2 的失败信号已明确不使用红色与脸部报错,此命令仅保留兼容。
|
||
/// <<expression_face_error 0.5 0.85>>
|
||
/// </summary>
|
||
[YarnCommand("expression_face_error")]
|
||
public static void ExpressionFaceError(float duration = 0.5f, float intensity = 0.85f)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayFaceErrorFlash(duration, intensity);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 结尾真假快速闪切(非阻塞,节奏由同长度 wait 控制):真心话与谎话交替独占屏幕,
|
||
/// 两者的屏幕底色(电视头背景)不同,越到后面切得越快,结束定格在真话帧。
|
||
/// <<expression_truth_lie_flicker "l10n.hs.exp.log1.tokens" "l10n.hs.exp.log1.target" 3.2 0.24 12>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_lie_flicker")]
|
||
public static IEnumerator ExpressionTruthLieFlicker(
|
||
string liePhrases,
|
||
string truthPhrase,
|
||
float duration,
|
||
float interval = 0.24f,
|
||
float fontSize = 12f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
liePhrases,
|
||
truthPhrase);
|
||
if (localized == null || localized.Length != 2 ||
|
||
!ValidateRequiredText(localized[0], "expression_truth_lie_flicker", nameof(liePhrases)) ||
|
||
!ValidateRequiredText(localized[1], "expression_truth_lie_flicker", nameof(truthPhrase)))
|
||
yield break;
|
||
|
||
presentation.StartTruthLieFlicker(localized[0], localized[1], duration, interval, fontSize);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定格的谎话被 glitch 翻转成真话(阻塞):大字乱跳中长度从谎话渐变到真话、真假底色抢闪,
|
||
/// 最后落在暖色真话。紧接 expression_memory_cut + expression_truth_snap 收尾。
|
||
/// <<expression_lie_morph_truth "l10n.hs.exp.atk.why" "l10n.hs.exp.log1.target" 0.9>>
|
||
/// </summary>
|
||
[YarnCommand("expression_lie_morph_truth")]
|
||
public static IEnumerator ExpressionLieMorphTruth(
|
||
string lieText,
|
||
string truthText,
|
||
float duration = 0.9f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
lieText,
|
||
truthText);
|
||
if (localized == null || localized.Length != 2 ||
|
||
!ValidateRequiredText(localized[0], "expression_lie_morph_truth", nameof(lieText)) ||
|
||
!ValidateRequiredText(localized[1], "expression_lie_morph_truth", nameof(truthText)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.MorphLieToTruth(localized[0], localized[1], duration));
|
||
}
|
||
|
||
[YarnCommand("expression_truth_leak")]
|
||
public static IEnumerator ExpressionTruthLeak(string fragment, float duration)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
fragment);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_truth_leak", nameof(fragment)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.LeakTruthFragment(localized[0], duration));
|
||
}
|
||
|
||
[YarnCommand("expression_lie_shuffle")]
|
||
public static IEnumerator ExpressionLieShuffle(
|
||
string characterPool,
|
||
float duration,
|
||
float interval)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
characterPool);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_lie_shuffle", nameof(characterPool)))
|
||
yield break;
|
||
|
||
presentation.StartLieCharacterShuffle(localized[0], duration, interval);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 演员大字闪现(阻塞):抖动的粒子字瞬间隐藏,屏幕中央出现白色大字(红青色散双影+急速缩放),
|
||
/// 停顿 holdDuration 秒后始终恢复粒子字抖动;结尾定格请使用 expression_actor_flash_in。
|
||
/// <<expression_actor_flash "l10n.hs.exp.atk.why" 0.6 10.5 1.15 1.60 0.03 0.22 0.5 0.2>>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_flash")]
|
||
public static IEnumerator ExpressionActorFlash(
|
||
string text,
|
||
float holdDuration = 0.6f,
|
||
float fontSize = 10.5f,
|
||
float settledScale = -1f,
|
||
float punchScale = -1f,
|
||
float chromaDistance = -1f,
|
||
float chromaAlpha = -1f,
|
||
float impact = -1f,
|
||
float glitchPeak = -1f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_flash", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.FlashActorLie(
|
||
localized[0],
|
||
holdDuration,
|
||
fontSize,
|
||
settledScale,
|
||
punchScale,
|
||
chromaDistance,
|
||
chromaAlpha,
|
||
impact,
|
||
glitchPeak));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 演员大字入场并保持(阻塞):入场表现与 expression_actor_flash 相同,但不执行退场,
|
||
/// 适合在段落结尾定格。后续清场命令仍会正常移除它。
|
||
/// <<expression_actor_flash_in "l10n.hs.exp.atk.why" 0.35 10.5 1.1 1.45 0.008 0.06 0.5 0>>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_flash_in")]
|
||
public static IEnumerator ExpressionActorFlashIn(
|
||
string text,
|
||
float holdDuration = 0.35f,
|
||
float fontSize = 10.5f,
|
||
float settledScale = -1f,
|
||
float punchScale = -1f,
|
||
float chromaDistance = -1f,
|
||
float chromaAlpha = -1f,
|
||
float impact = -1f,
|
||
float glitchPeak = -1f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_flash_in", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.FlashActorLie(
|
||
localized[0],
|
||
holdDuration,
|
||
fontSize,
|
||
settledScale,
|
||
punchScale,
|
||
chromaDistance,
|
||
chromaAlpha,
|
||
impact,
|
||
glitchPeak,
|
||
true));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 真话闪现(阻塞):谎话大字之间,暖色完整真话带细微抖动挣扎浮现一拍;
|
||
/// "被掐断"由紧随其后的 expression_glitch_pulse 表现。不推进谎话冲击等级。
|
||
/// <<expression_actor_truth_flash "l10n.hs.exp.log1.target" 0.35 8.5 0.55 0.02>>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_truth_flash")]
|
||
public static IEnumerator ExpressionActorTruthFlash(
|
||
string text,
|
||
float holdDuration = 0.35f,
|
||
float fontSize = 8.5f,
|
||
float alpha = 0.8f,
|
||
float jitter = 0.02f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_truth_flash", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.FlashActorTruth(localized[0], holdDuration, fontSize, alpha, jitter));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 老虎机换字(非阻塞,节拍由 Yarn wait 控制):聚焦字在 duration 内只从 charPool 里疯狂换字。
|
||
/// truthPool 非空时其字符也混入换字池,并以真话暖色显示。
|
||
/// <<expression_actor_slot "l10n.hs.exp.log1.tokens" 5 0.05 14 "l10n.hs.exp.log1.target">>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_slot")]
|
||
public static IEnumerator ExpressionActorSlot(
|
||
string charPool,
|
||
float duration,
|
||
float interval = 0.05f,
|
||
float fontSize = 14f,
|
||
string truthPool = "")
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
charPool,
|
||
truthPool);
|
||
if (localized == null || localized.Length != 2 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_slot", nameof(charPool)))
|
||
yield break;
|
||
|
||
presentation.StartActorSlotMachine(
|
||
localized[0],
|
||
duration,
|
||
interval,
|
||
fontSize,
|
||
localized[1] ?? string.Empty);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将 Glitch、记忆冲击与画面推近缓慢堆到峰值,停住后保持峰值,等待后续硬切。
|
||
/// <<expression_overload_peak 0.75 0.22 1.10 1 1>>
|
||
/// </summary>
|
||
[YarnCommand("expression_overload_peak")]
|
||
public static IEnumerator ExpressionOverloadPeak(
|
||
float riseDuration = 0.75f,
|
||
float holdDuration = 0.22f,
|
||
float zoom = 1.10f,
|
||
float glitchPeak = 1f,
|
||
float impact = 1f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.BuildActorOverloadPeak(
|
||
riseDuration,
|
||
holdDuration,
|
||
zoom,
|
||
glitchPeak,
|
||
impact));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单字/短词满屏重击,并等待该拍结束。
|
||
/// <<expression_actor_word_hit "l10n.hs.exp.atk.why" 0.16 18 1.55>>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_word_hit")]
|
||
public static IEnumerator ExpressionActorWordHit(
|
||
string text,
|
||
float holdDuration = 0.16f,
|
||
float fontSize = 18f,
|
||
float punchScale = 1.55f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_word_hit", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.PlayActorWordHit(localized[0], holdDuration, fontSize, punchScale));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 单列词组严格裁在表情屏内,像字幕一样无缝向下滚动。
|
||
/// <<expression_actor_scroll "l10n.hs.exp.log1.tokens" 1.8 0.85 0.45 5.2>>
|
||
/// </summary>
|
||
[YarnCommand("expression_actor_scroll")]
|
||
public static IEnumerator ExpressionActorScroll(
|
||
string phraseList,
|
||
float scrollDuration = 1.8f,
|
||
float loopDuration = 0.85f,
|
||
float holdDuration = 0.45f,
|
||
float fontSize = 5.2f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
phraseList);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_actor_scroll", nameof(phraseList)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.ScrollActorLies(
|
||
localized[0],
|
||
scrollDuration,
|
||
loopDuration,
|
||
holdDuration,
|
||
fontSize));
|
||
}
|
||
|
||
[YarnCommand("expression_memory_cut")]
|
||
public static void ExpressionMemoryCut()
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.CutMemoryAndGlitchImmediate();
|
||
}
|
||
|
||
[YarnCommand("expression_truth_snap")]
|
||
public static void ExpressionTruthSnap(float peripheral, float calmRadius)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.SnapTruthImmediate(peripheral, calmRadius);
|
||
}
|
||
|
||
[YarnCommand("expression_glitch_pulse")]
|
||
public static void ExpressionGlitchPulse(float peak, float riseDuration, float fallDuration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayGlitchPulse(peak, riseDuration, fallDuration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 火山「屏幕」绝对档位(非阻塞)。作用在屏幕材质,不是叠一层噪波。
|
||
/// 会清掉当前脉冲并把底压直接设成 <paramref name="level"/>;到位后一直保持。
|
||
/// 逐句积蓄情绪请改用 expression_screen_stress / expression_screen_pulse,
|
||
/// 这条命令保留给"我就是要现在正好这个档位"的场合。
|
||
/// <<expression_screen_glitch 0.55 1.2>> … <<expression_screen_glitch 0 1.5>>
|
||
/// </summary>
|
||
[YarnCommand("expression_screen_glitch")]
|
||
public static void ExpressionScreenGlitch(
|
||
float level = 0.5f,
|
||
float duration = 1f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] expression_screen_glitch 找不到 ExpressionManager;已安全跳过。");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.SetScreenGlitchLevel(level, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 屏幕蓄压(非阻塞):往底压罐里加一口,只升不降,这就是"积蓄的情绪"。
|
||
/// 每次情绪推进加一次,不用记上次是多少档;有软上限,堆不满。
|
||
/// <<expression_screen_stress 0.15>>
|
||
/// </summary>
|
||
/// <param name="amount">这次加多少(建议 0.08~0.2)</param>
|
||
/// <param name="duration">爬升到新底压的时间</param>
|
||
[YarnCommand("expression_screen_stress")]
|
||
public static void ExpressionScreenStress(float amount = 0.15f, float duration = 0.8f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] expression_screen_stress 找不到 ExpressionManager;已安全跳过。");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.AddScreenStress(amount, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 屏幕冲击(非阻塞):瞬间顶到「底压 + strength」,随后自动落回底压。
|
||
/// 一句狠话打一记;底压很低的开场也能打得很响,打完自己回到安静。
|
||
/// <<expression_screen_pulse 0.6>>
|
||
/// </summary>
|
||
/// <param name="strength">冲击高度(建议 0.3~0.8)</param>
|
||
/// <param name="decay">落回底压的时间</param>
|
||
[YarnCommand("expression_screen_pulse")]
|
||
public static void ExpressionScreenPulse(float strength = 0.5f, float decay = 1.6f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] expression_screen_pulse 找不到 ExpressionManager;已安全跳过。");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.PulseScreenGlitch(strength, decay);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 屏幕转红(非阻塞):荧光色从青绿推向愤怒红,LOG3 攻击段用。
|
||
/// 只换颜色不改故障强度,配合 expression_screen_pulse 打 punch。
|
||
/// <<expression_screen_rage 1 0.4>> … <<expression_screen_rage 0 1>>
|
||
/// </summary>
|
||
[YarnCommand("expression_screen_rage")]
|
||
public static void ExpressionScreenRage(float level = 1f, float duration = 0.5f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] expression_screen_rage 找不到 ExpressionManager;已安全跳过。");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.SetScreenRage(level, duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 屏幕泄压(非阻塞):底压和脉冲一起放掉归零,情绪落幕时用。
|
||
/// <<expression_screen_release 1.2>>
|
||
/// </summary>
|
||
[YarnCommand("expression_screen_release")]
|
||
public static void ExpressionScreenRelease(float duration = 1.2f)
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError(
|
||
"[LanguageYarnCommand] expression_screen_release 找不到 ExpressionManager;已安全跳过。");
|
||
return;
|
||
}
|
||
|
||
ExpressionManager.ReleaseScreenStress(duration);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 用现有目标粒子完整显示一个红色攻击词,并等待确定性的冲击动作结束。
|
||
/// 超过目标粒子数量时会安全跳过,不会截断。
|
||
/// <<expression_truth_attack "l10n.hs.exp.atk.why" 0.34 0.45>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_attack")]
|
||
public static IEnumerator ExpressionTruthAttack(
|
||
string text,
|
||
float duration = 0.34f,
|
||
float impact = 0.5f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_truth_attack", nameof(text)))
|
||
yield break;
|
||
|
||
// 红屏武装时每个攻击词都把屏幕顶一下(punch),力度跟着这个词的 impact 走。
|
||
// 写在这里而不是 Yarn 里逐条加 pulse:攻击列表经常重调,两边手写必然脱节。
|
||
ExpressionManager.PunchScreenForAttack(impact);
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.PlayTruthAttack(localized[0], duration, impact));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Keep an already resolved truth phrase in place while it gradually becomes unstable and red.
|
||
/// <<expression_truth_destabilize "l10n.hs.exp.log3.target" 1.6 0.13 22 "FF304D">>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_destabilize")]
|
||
public static IEnumerator ExpressionTruthDestabilize(
|
||
string text,
|
||
float duration = 1.6f,
|
||
float shakeAmplitude = 0.13f,
|
||
float shakeSpeed = 22f,
|
||
string colorHex = "FF304D")
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_truth_destabilize", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.DestabilizeTruthPhrase(
|
||
localized[0],
|
||
duration,
|
||
shakeAmplitude,
|
||
shakeSpeed,
|
||
colorHex));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 将当前攻击词重组为固定青绿色真话,并等待稳定完成。
|
||
/// <<expression_truth_settle "l10n.hs.exp.final.win" 0.8>>
|
||
/// </summary>
|
||
[YarnCommand("expression_truth_settle")]
|
||
public static IEnumerator ExpressionTruthSettle(
|
||
string text,
|
||
float duration = 0.8f)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
|
||
string[] localized = null;
|
||
yield return ResolveTextReferences(
|
||
GetCommandLocale(),
|
||
values => localized = values,
|
||
text);
|
||
if (localized == null || localized.Length != 1 ||
|
||
!ValidateRequiredText(localized[0], "expression_truth_settle", nameof(text)))
|
||
yield break;
|
||
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.SettleTruthPhrase(localized[0], duration));
|
||
}
|
||
|
||
[YarnCommand("expression_truth_blackout")]
|
||
public static IEnumerator ExpressionTruthBlackout(float duration, float alpha)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.HoldTruthBlackout(duration, alpha));
|
||
}
|
||
|
||
[YarnCommand("expression_lie_split")]
|
||
public static void ExpressionLieSplit(
|
||
float duration,
|
||
float distanceRatio,
|
||
float stretchX,
|
||
float squashY)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
{
|
||
presentation.SplitLieVisual(
|
||
duration,
|
||
distanceRatio,
|
||
stretchX,
|
||
squashY);
|
||
}
|
||
}
|
||
|
||
[YarnCommand("expression_truth_flash")]
|
||
public static void ExpressionTruthFlash(
|
||
float alpha,
|
||
float riseDuration,
|
||
float holdDuration,
|
||
float fallDuration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
{
|
||
presentation.PlayTruthFlash(
|
||
alpha,
|
||
riseDuration,
|
||
holdDuration,
|
||
fallDuration);
|
||
}
|
||
}
|
||
|
||
[YarnCommand("expression_memory_brightness_pulse")]
|
||
public static void ExpressionMemoryBrightnessPulse(
|
||
float amount,
|
||
float riseDuration,
|
||
float fallDuration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayMemoryBrightnessPulse(amount, riseDuration, fallDuration);
|
||
}
|
||
|
||
[YarnCommand("expression_memory_tear")]
|
||
public static void ExpressionMemoryTear(float peak, float duration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayMemoryTear(peak, duration);
|
||
}
|
||
|
||
[YarnCommand("expression_face_dip")]
|
||
public static void ExpressionFaceDip(float duration, float ratio)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.PlayFaceDip(duration, ratio);
|
||
}
|
||
|
||
[YarnCommand("expression_truth_release")]
|
||
public static void ExpressionTruthRelease(float alphaDuration)
|
||
{
|
||
if (TryGetPresentation(out LogReleasePresentationController presentation))
|
||
presentation.ReleaseTruthCharacters(alphaDuration);
|
||
}
|
||
|
||
[YarnCommand("expression_truth_resolve")]
|
||
public static IEnumerator ExpressionTruthResolve(
|
||
float duration,
|
||
float peripheral,
|
||
float calmRadius)
|
||
{
|
||
if (!TryGetPresentation(out LogReleasePresentationController presentation))
|
||
yield break;
|
||
yield return ExpressionManager.StartCoroutine(
|
||
presentation.ResolveTruthCharacters(duration, peripheral, calmRadius));
|
||
}
|
||
|
||
[YarnCommand("expression_memory_end")]
|
||
public static IEnumerator ExpressionMemoryEnd()
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
yield break;
|
||
}
|
||
yield return ExpressionManager.StartCoroutine(ExpressionManager.EndExpressionMemory());
|
||
}
|
||
|
||
[YarnCommand("expression_truth_impact")]
|
||
public static void ExpressionTruthImpact()
|
||
{
|
||
if (ExpressionManager == null)
|
||
{
|
||
UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
return;
|
||
}
|
||
ExpressionManager.ImpactExpressionTruth();
|
||
}
|
||
|
||
// /// <summary>
|
||
// /// Glitch 噪波过渡(指定起止值),等待完成后再继续下一步。
|
||
// /// </summary>
|
||
// [YarnCommand("glitch_transition")]
|
||
// public static IEnumerator GlitchTransition(float from, float to, float duration)
|
||
// {
|
||
// if (ExpressionManager == null)
|
||
// {
|
||
// UnityEngine.Debug.LogError("ExpressionManager 未找到!");
|
||
// yield break;
|
||
// }
|
||
// yield return ExpressionManager.StartCoroutine(ExpressionManager.TransitionGlitchAndWait(from, to, duration));
|
||
// }
|
||
}
|
||
}
|