Skip to content

Commit 38dc468

Browse files
committed
Add quest check confirmation when switching worlds
Introduce a confirmation UI when entering a different world to prevent accidental quest progress errors, allowing players to choose whether to track quests in the current world. Enhances user experience and data safety in multi-world scenarios, with full localization and clear visual feedback.
1 parent 566d10f commit 38dc468

9 files changed

+1050
-5
lines changed

Content/QuestLogs/QLPlayer.cs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using CalamityOverhaul.Content.QuestLogs.Core;
1+
using CalamityOverhaul.Common;
2+
using CalamityOverhaul.Content.QuestLogs.Core;
23
using CalamityOverhaul.Content.QuestLogs.QLNodes;
4+
using InnoVault.GameSystem;
35
using System;
46
using System.Collections.Generic;
57
using Terraria;
@@ -12,6 +14,18 @@ internal class QLPlayer : ModPlayer
1214
{
1315
public Dictionary<string, QuestSaveData> QuestProgress = new();
1416

17+
/// <summary>
18+
/// 上次检测任务的世界完整名称
19+
/// </summary>
20+
public string LastWorldFullName = string.Empty;
21+
22+
/// <summary>
23+
/// 在此世界中跳过任务检测(用于用户选择跳过后记录)
24+
/// </summary>
25+
public string DontCheckQuestInWorld = string.Empty;
26+
27+
public override bool IsLoadingEnabled(Mod mod) => CWRServerConfig.Instance.QuestLog;
28+
1529
public override void SaveData(TagCompound tag) {
1630
try {
1731
QuestProgress ??= [];
@@ -20,6 +34,14 @@ public override void SaveData(TagCompound tag) {
2034
questsTag[kvp.Key] = kvp.Value.Serialize();
2135
}
2236
tag["QuestProgress"] = questsTag;
37+
38+
//保存世界追踪数据
39+
if (!string.IsNullOrEmpty(LastWorldFullName)) {
40+
tag["QL_LastWorldFullName"] = LastWorldFullName;
41+
}
42+
if (!string.IsNullOrEmpty(DontCheckQuestInWorld)) {
43+
tag["QL_DontCheckQuestInWorld"] = DontCheckQuestInWorld;
44+
}
2345
} catch (Exception ex) {
2446
CWRMod.Instance.Logger.Error($"[QLPlayer:SaveData] an error has occurred:{ex.Message}");
2547
}
@@ -36,6 +58,16 @@ public override void LoadData(TagCompound tag) {
3658
}
3759
}
3860
}
61+
62+
//加载世界追踪数据
63+
LastWorldFullName = string.Empty;
64+
if (tag.TryGet("QL_LastWorldFullName", out string lastWorld)) {
65+
LastWorldFullName = lastWorld;
66+
}
67+
DontCheckQuestInWorld = string.Empty;
68+
if (tag.TryGet("QL_DontCheckQuestInWorld", out string dontCheck)) {
69+
DontCheckQuestInWorld = dontCheck;
70+
}
3971
} catch (Exception ex) {
4072
CWRMod.Instance.Logger.Error($"[QLPlayer:LoadData] an error has occurred:{ex.Message}");
4173
}
@@ -48,7 +80,36 @@ public QuestSaveData GetQuestData(string questID) {
4880
return QuestProgress[questID];
4981
}
5082

83+
/// <summary>
84+
/// 检查是否应该在当前世界检测任务
85+
/// </summary>
86+
public bool ShouldCheckQuestInCurrentWorld() {
87+
//如果用户选择了跳过当前世界的任务检测
88+
if (DontCheckQuestInWorld == SaveWorld.WorldFullName) {
89+
return false;
90+
}
91+
return true;
92+
}
93+
5194
public override void OnEnterWorld() {
95+
string currentWorldFullName = SaveWorld.WorldFullName;
96+
97+
//每次进入世界都重置跳过标记,确保每次进入都会提醒
98+
//只有当用户在本次会话中选择跳过后才会设置DontCheckQuestInWorld
99+
//这样下次进入世界时会重新询问
100+
101+
//检测是否进入了不同的世界
102+
if (!string.IsNullOrEmpty(LastWorldFullName) && LastWorldFullName != currentWorldFullName) {
103+
//进入了不同的世界,重置跳过标记并弹出确认窗口
104+
DontCheckQuestInWorld = string.Empty;
105+
QuestWorldConfirmUI.RequestConfirm(Main.worldName, LastWorldFullName);
106+
}
107+
else if (string.IsNullOrEmpty(LastWorldFullName)) {
108+
//首次进入,正常设置
109+
LastWorldFullName = currentWorldFullName;
110+
}
111+
//同一世界不需要重置,保持之前的选择
112+
52113
if (QuestNode.GetQuest<FirstQuest>() != null) {
53114
QuestNode.GetQuest<FirstQuest>().IsUnlocked = true;
54115
}
@@ -65,6 +126,16 @@ public override void PostUpdate() {
65126
return;
66127
}
67128

129+
//如果用户跳过了当前世界的任务检测,则不更新任务
130+
if (!ShouldCheckQuestInCurrentWorld()) {
131+
return;
132+
}
133+
134+
//如果确认窗口正在显示,暂停任务更新
135+
if (QuestWorldConfirmUI.Instance != null && QuestWorldConfirmUI.Instance.Active) {
136+
return;
137+
}
138+
68139
//每60帧检查一次未解锁的任务,防止漏掉
69140
bool checkUnlock = Main.GameUpdateCount % 60 == 0 && QuestLog.Instance.visible;
70141

Content/QuestLogs/QuestLog.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using Microsoft.Xna.Framework.Graphics;
66
using Microsoft.Xna.Framework.Input;
77
using ReLogic.Content;
8+
using System;
89
using System.Collections.Generic;
910
using Terraria;
1011
using Terraria.Audio;
@@ -562,6 +563,85 @@ public override void Draw(SpriteBatch spriteBatch) {
562563
if (nightHovered) {
563564
Main.hoverItemName = NightMode ? NightModeText.Value : SunModeText.Value;
564565
}
566+
567+
//如果任务检测被禁用,绘制禁止覆盖层
568+
var qlPlayer = Main.LocalPlayer.GetModPlayer<QLPlayer>();
569+
if (!qlPlayer.ShouldCheckQuestInCurrentWorld()) {
570+
DrawDisabledOverlay(spriteBatch);
571+
}
572+
}
573+
574+
private float disabledOverlayAnimTime;
575+
576+
private void DrawDisabledOverlay(SpriteBatch spriteBatch) {
577+
disabledOverlayAnimTime += 0.016f;
578+
579+
Texture2D pixel = VaultAsset.placeholder2.Value;
580+
581+
//半透明红色覆盖层
582+
float pulseAlpha = 0.65f + MathF.Sin(disabledOverlayAnimTime * 2f) * 0.05f;
583+
Color overlayColor = new Color(150, 50, 50) * (mainPanelAlpha * pulseAlpha);
584+
spriteBatch.Draw(pixel, panelRect, overlayColor);
585+
586+
//绘制禁止符号
587+
Vector2 center = new Vector2(panelRect.X + panelRect.Width / 2f, panelRect.Y + panelRect.Height / 2f);
588+
589+
//外圆
590+
float circleRadius = 60f;
591+
float circleThickness = 8f;
592+
Color circleColor = new Color(200, 60, 60) * (mainPanelAlpha * 0.8f);
593+
594+
//使用SoftGlow绘制发光效果
595+
Texture2D softGlow = CWRAsset.SoftGlow.Value;
596+
float glowPulse = 0.8f + MathF.Sin(disabledOverlayAnimTime * 3f) * 0.2f;
597+
Color glowColor = new Color(200, 80, 80, 0) * (mainPanelAlpha * 0.4f * glowPulse);
598+
spriteBatch.Draw(softGlow, center, null, glowColor, 0f,
599+
softGlow.Size() / 2f, 2f, SpriteEffects.None, 0f);
600+
601+
//绘制圆环
602+
int segments = 36;
603+
for (int i = 0; i < segments; i++) {
604+
float angle1 = MathHelper.TwoPi * i / segments;
605+
float angle2 = MathHelper.TwoPi * (i + 1) / segments;
606+
607+
Vector2 p1 = center + angle1.ToRotationVector2() * circleRadius;
608+
Vector2 p2 = center + angle2.ToRotationVector2() * circleRadius;
609+
610+
float segAngle = MathF.Atan2(p2.Y - p1.Y, p2.X - p1.X);
611+
float segLength = Vector2.Distance(p1, p2);
612+
613+
spriteBatch.Draw(pixel, p1, new Rectangle(0, 0, 1, 1), circleColor,
614+
segAngle, new Vector2(0, 0.5f), new Vector2(segLength + 1, circleThickness), SpriteEffects.None, 0f);
615+
}
616+
617+
//绘制斜线(禁止符号)
618+
float lineAngle = MathHelper.PiOver4;
619+
float lineLength = circleRadius * 1.4f;
620+
Vector2 lineStart = center - lineAngle.ToRotationVector2() * lineLength / 2f;
621+
622+
spriteBatch.Draw(pixel, lineStart, new Rectangle(0, 0, 1, 1), circleColor,
623+
lineAngle, new Vector2(0, 0.5f), new Vector2(lineLength, circleThickness), SpriteEffects.None, 0f);
624+
625+
//绘制提示文本
626+
string text = QuestWorldConfirmUI.DisabledOverlayText?.Value ?? "任务检测已被禁止";
627+
string[] lines = text.Split('\n');
628+
629+
float textY = center.Y + circleRadius + 30f;
630+
float lineHeight = FontAssets.MouseText.Value.MeasureString("A").Y;
631+
632+
for (int i = 0; i < lines.Length; i++) {
633+
Vector2 textSize = FontAssets.MouseText.Value.MeasureString(lines[i]);
634+
Vector2 textPos = new Vector2(center.X - textSize.X / 2f * 0.9f, textY + i * lineHeight);
635+
636+
//文字阴影
637+
Utils.DrawBorderString(spriteBatch, lines[i], textPos + new Vector2(2, 2),
638+
Color.Black * (mainPanelAlpha * 0.6f), 0.9f);
639+
640+
//文字主体(带脉冲效果)
641+
Color textColor = Color.Lerp(new Color(255, 180, 180), new Color(255, 100, 100),
642+
MathF.Sin(disabledOverlayAnimTime * 2f) * 0.5f + 0.5f);
643+
Utils.DrawBorderString(spriteBatch, lines[i], textPos, textColor * mainPanelAlpha, 0.9f);
644+
}
565645
}
566646

567647
private void DrawMainCloseButton(SpriteBatch spriteBatch) {

0 commit comments

Comments
 (0)