1
This commit is contained in:
@@ -15,9 +15,10 @@ namespace WulaFallenEmpire
|
||||
// 图形缓存 - 现在包含Shader信息
|
||||
private Dictionary<string, Graphic> graphicsCache = new Dictionary<string, Graphic>();
|
||||
|
||||
// 动画状态 - 每个图层的独立浮动
|
||||
// 动画状态 - 每个图层的独立状态
|
||||
private Dictionary<int, float> layerHoverOffsets = new Dictionary<int, float>();
|
||||
private Dictionary<int, float> layerAnimationTimes = new Dictionary<int, float>();
|
||||
private Dictionary<int, float> layerRotationAngles = new Dictionary<int, float>();
|
||||
private int lastTick = -1;
|
||||
|
||||
public ExtraGraphicsExtension ModExtension
|
||||
@@ -122,8 +123,8 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
// 不调用基类的 DrawAt,完全自定义渲染
|
||||
|
||||
// 更新悬浮动画
|
||||
UpdateHoverAnimation();
|
||||
// 更新动画状态
|
||||
UpdateAnimations();
|
||||
// 绘制所有配置的图形层
|
||||
DrawGraphicLayers(drawLoc, flip);
|
||||
// 新增:绘制护盾
|
||||
@@ -152,7 +153,7 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制单个图形层
|
||||
// 绘制单个图形层 - 现在支持旋转动画
|
||||
private void DrawGraphicLayer(Vector3 baseDrawPos, bool flip, GraphicLayerData layer)
|
||||
{
|
||||
if (string.IsNullOrEmpty(layer.texturePath))
|
||||
@@ -167,27 +168,89 @@ namespace WulaFallenEmpire
|
||||
// 获取图形(现在传入Shader)
|
||||
Graphic graphic = GetCachedGraphic(layer.texturePath, layer.scale, layer.color, shader);
|
||||
|
||||
// 计算图层浮动偏移
|
||||
float hoverOffset = 0f;
|
||||
if (layer.enableHover)
|
||||
// 计算图层动画偏移
|
||||
Vector3 animationOffset = Vector3.zero;
|
||||
float rotationAngle = 0f;
|
||||
int layerIndex = ModExtension.graphicLayers.IndexOf(layer);
|
||||
|
||||
// 根据动画类型应用不同的动画效果
|
||||
switch (layer.animationType)
|
||||
{
|
||||
int layerIndex = ModExtension.graphicLayers.IndexOf(layer);
|
||||
if (layerHoverOffsets.ContainsKey(layerIndex))
|
||||
{
|
||||
hoverOffset = layerHoverOffsets[layerIndex];
|
||||
}
|
||||
case AnimationType.Hover:
|
||||
if (layer.enableAnimation && layerHoverOffsets.ContainsKey(layerIndex))
|
||||
{
|
||||
animationOffset.z = layerHoverOffsets[layerIndex];
|
||||
}
|
||||
break;
|
||||
|
||||
case AnimationType.Rotate:
|
||||
if (layer.enableAnimation && layerRotationAngles.ContainsKey(layerIndex))
|
||||
{
|
||||
rotationAngle = layerRotationAngles[layerIndex];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 最终绘制位置 = 基础位置 + 图层偏移 + 浮动偏移
|
||||
Vector3 drawPos = baseDrawPos + layer.offset;
|
||||
drawPos.z += hoverOffset;
|
||||
|
||||
// 绘制图形
|
||||
graphic.Draw(drawPos, flip ? base.Rotation.Opposite : base.Rotation, this, 0f);
|
||||
// 最终绘制位置 = 基础位置 + 图层偏移 + 动画偏移
|
||||
Vector3 drawPos = baseDrawPos + layer.offset + animationOffset;
|
||||
|
||||
// 如果启用了旋转动画,使用矩阵变换绘制
|
||||
if (layer.animationType == AnimationType.Rotate && layer.enableAnimation && rotationAngle != 0f)
|
||||
{
|
||||
DrawWithRotation(graphic, drawPos, flip, rotationAngle, layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通绘制
|
||||
graphic.Draw(drawPos, flip ? base.Rotation.Opposite : base.Rotation, this, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
// 更新每个图层的独立悬浮动画
|
||||
private void UpdateHoverAnimation()
|
||||
// 使用矩阵变换绘制旋转图形
|
||||
private void DrawWithRotation(Graphic graphic, Vector3 drawPos, bool flip, float rotationAngle, GraphicLayerData layer)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取网格和材质
|
||||
Mesh mesh = graphic.MeshAt(flip ? base.Rotation.Opposite : base.Rotation);
|
||||
Material mat = graphic.MatAt(flip ? base.Rotation.Opposite : base.Rotation);
|
||||
|
||||
if (mesh == null || mat == null)
|
||||
{
|
||||
Log.Warning($"Building_ExtraGraphics: Unable to get mesh or material for rotating layer");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建旋转矩阵
|
||||
Quaternion rotation = Quaternion.Euler(0f, 0f, rotationAngle);
|
||||
|
||||
// 如果图层有旋转中心偏移,需要调整位置
|
||||
Vector3 pivotOffset = new Vector3(layer.pivotOffset.x, layer.pivotOffset.y, 0f);
|
||||
|
||||
// 计算最终矩阵
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(
|
||||
drawPos + pivotOffset, // 位置
|
||||
rotation, // 旋转
|
||||
new Vector3(layer.scale.x, layer.scale.y, 1f) // 缩放
|
||||
);
|
||||
|
||||
// 绘制网格
|
||||
Graphics.DrawMesh(mesh, matrix, mat, 0);
|
||||
|
||||
// 如果需要,绘制第二面(双面渲染)
|
||||
if (layer.doubleSided)
|
||||
{
|
||||
Graphics.DrawMesh(mesh, matrix, mat, 0, null, 0, null, UnityEngine.Rendering.ShadowCastingMode.Off, true);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error($"Building_ExtraGraphics: Error drawing rotating layer: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
// 更新所有图层的动画状态
|
||||
private void UpdateAnimations()
|
||||
{
|
||||
int currentTick = Find.TickManager.TicksGame;
|
||||
|
||||
@@ -198,23 +261,47 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
var layer = ModExtension.graphicLayers[i];
|
||||
|
||||
if (layer.enableHover)
|
||||
if (!layer.enableAnimation)
|
||||
continue;
|
||||
|
||||
// 初始化动画时间
|
||||
if (!layerAnimationTimes.ContainsKey(i))
|
||||
{
|
||||
// 初始化动画时间
|
||||
if (!layerAnimationTimes.ContainsKey(i))
|
||||
{
|
||||
layerAnimationTimes[i] = 0f;
|
||||
}
|
||||
|
||||
// 更新动画时间
|
||||
layerAnimationTimes[i] += Time.deltaTime;
|
||||
|
||||
// 计算该图层的悬浮偏移
|
||||
float hoverSpeed = layer.hoverSpeed > 0 ? layer.hoverSpeed : ModExtension.globalHoverSpeed;
|
||||
float hoverIntensity = layer.hoverIntensity > 0 ? layer.hoverIntensity : ModExtension.globalHoverIntensity;
|
||||
|
||||
float hoverOffset = Mathf.Sin(layerAnimationTimes[i] * hoverSpeed + layer.hoverPhase) * hoverIntensity;
|
||||
layerHoverOffsets[i] = hoverOffset;
|
||||
layerAnimationTimes[i] = layer.animationStartTime;
|
||||
}
|
||||
|
||||
// 更新动画时间
|
||||
layerAnimationTimes[i] += Time.deltaTime;
|
||||
|
||||
// 根据动画类型更新不同的状态
|
||||
switch (layer.animationType)
|
||||
{
|
||||
case AnimationType.Hover:
|
||||
// 计算该图层的悬浮偏移
|
||||
float hoverSpeed = layer.animationSpeed > 0 ? layer.animationSpeed : ModExtension.globalAnimationSpeed;
|
||||
float hoverIntensity = layer.animationIntensity > 0 ? layer.animationIntensity : ModExtension.globalAnimationIntensity;
|
||||
|
||||
float hoverOffset = Mathf.Sin(layerAnimationTimes[i] * hoverSpeed + layer.animationPhase) * hoverIntensity;
|
||||
layerHoverOffsets[i] = hoverOffset;
|
||||
break;
|
||||
|
||||
case AnimationType.Rotate:
|
||||
// 计算该图层的旋转角度
|
||||
float rotateSpeed = layer.animationSpeed > 0 ? layer.animationSpeed : ModExtension.globalAnimationSpeed;
|
||||
float maxAngle = layer.animationIntensity > 0 ? layer.animationIntensity : ModExtension.globalAnimationIntensity;
|
||||
|
||||
// 旋转角度(循环)
|
||||
float rotationAngle = (layerAnimationTimes[i] * rotateSpeed * 360f) % 360f;
|
||||
|
||||
// 限制旋转角度范围(如果设置了最大角度)
|
||||
if (maxAngle > 0 && maxAngle < 360f)
|
||||
{
|
||||
// 使用正弦波限制旋转角度范围
|
||||
rotationAngle = Mathf.Sin(layerAnimationTimes[i] * rotateSpeed) * maxAngle;
|
||||
}
|
||||
|
||||
layerRotationAngles[i] = rotationAngle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,12 +317,20 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
// 动画类型枚举
|
||||
public enum AnimationType
|
||||
{
|
||||
None, // 无动画
|
||||
Hover, // 上下浮动
|
||||
Rotate // 自旋转
|
||||
}
|
||||
|
||||
// 主要的 ModExtension 定义
|
||||
public class ExtraGraphicsExtension : DefModExtension
|
||||
{
|
||||
// 全局悬浮参数(作为默认值)
|
||||
public float globalHoverSpeed = 2f; // 全局悬浮速度
|
||||
public float globalHoverIntensity = 0.1f; // 全局悬浮强度
|
||||
// 全局动画参数(作为默认值)
|
||||
public float globalAnimationSpeed = 2f; // 全局动画速度
|
||||
public float globalAnimationIntensity = 0.1f; // 全局动画强度
|
||||
|
||||
// 图形层配置
|
||||
public List<GraphicLayerData> graphicLayers = new List<GraphicLayerData>();
|
||||
@@ -250,7 +345,7 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
|
||||
// 单个图形层的配置数据 - 添加shaderName字段
|
||||
// 单个图形层的配置数据 - 增强版
|
||||
public class GraphicLayerData
|
||||
{
|
||||
// 基础配置
|
||||
@@ -258,16 +353,56 @@ namespace WulaFallenEmpire
|
||||
public Vector2 scale = Vector2.one; // 缩放比例
|
||||
public Color color = Color.white; // 颜色
|
||||
public int drawOrder = 0; // 绘制顺序(数字小的先绘制)
|
||||
public string shaderName = "TransparentPostLight"; // Shader名称(新增)
|
||||
public string shaderName = "TransparentPostLight"; // Shader名称
|
||||
|
||||
// 位置配置 - 使用环世界坐标系
|
||||
// X: 左右偏移, Y: 图层深度, Z: 上下偏移
|
||||
public Vector3 offset = Vector3.zero;
|
||||
|
||||
// 独立悬浮配置
|
||||
public bool enableHover = true; // 是否启用悬浮
|
||||
public float hoverSpeed = 0f; // 悬浮速度(0表示使用全局速度)
|
||||
public float hoverIntensity = 0f; // 悬浮强度(0表示使用全局强度)
|
||||
public float hoverPhase = 0f; // 悬浮相位(用于错开浮动)
|
||||
// 动画配置
|
||||
public AnimationType animationType = AnimationType.Hover; // 动画类型
|
||||
public bool enableAnimation = true; // 是否启用动画
|
||||
public float animationSpeed = 0f; // 动画速度(0表示使用全局速度)
|
||||
public float animationIntensity = 0f; // 动画强度(0表示使用全局强度)
|
||||
public float animationPhase = 0f; // 动画相位(用于错开动画)
|
||||
public float animationStartTime = 0f; // 动画开始时间偏移
|
||||
|
||||
// 旋转动画专用配置
|
||||
public Vector2 pivotOffset = Vector2.zero; // 旋转中心偏移(相对于图层的中心)
|
||||
public bool doubleSided = false; // 是否双面渲染(对于旋转物体)
|
||||
|
||||
// 兼容旧字段(为了向后兼容)
|
||||
[Obsolete("Use enableAnimation instead")]
|
||||
public bool enableHover
|
||||
{
|
||||
get => enableAnimation && animationType == AnimationType.Hover;
|
||||
set
|
||||
{
|
||||
enableAnimation = value;
|
||||
if (value && animationType == AnimationType.None)
|
||||
animationType = AnimationType.Hover;
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Use animationSpeed instead")]
|
||||
public float hoverSpeed
|
||||
{
|
||||
get => animationSpeed;
|
||||
set => animationSpeed = value;
|
||||
}
|
||||
|
||||
[Obsolete("Use animationIntensity instead")]
|
||||
public float hoverIntensity
|
||||
{
|
||||
get => animationIntensity;
|
||||
set => animationIntensity = value;
|
||||
}
|
||||
|
||||
[Obsolete("Use animationPhase instead")]
|
||||
public float hoverPhase
|
||||
{
|
||||
get => animationPhase;
|
||||
set => animationPhase = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,15 +201,22 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
if (MechPawn == null || !CanBeAutonomous)
|
||||
yield break;
|
||||
|
||||
|
||||
// 工作模式切换按钮
|
||||
if (CanWorkAutonomously)
|
||||
{
|
||||
yield return new DroneGizmo(this);
|
||||
DroneGizmo droneGizmo = new DroneGizmo(this);
|
||||
if (droneGizmo != null)
|
||||
{
|
||||
yield return droneGizmo;
|
||||
}
|
||||
}
|
||||
// 更换武器按钮 - 确保不返回null
|
||||
Gizmo weaponSwitchGizmo = CreateWeaponSwitchGizmo();
|
||||
if (weaponSwitchGizmo != null)
|
||||
{
|
||||
yield return weaponSwitchGizmo;
|
||||
}
|
||||
|
||||
// 更换武器按钮
|
||||
yield return CreateWeaponSwitchGizmo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -217,20 +224,43 @@ namespace WulaFallenEmpire
|
||||
/// </summary>
|
||||
private Gizmo CreateWeaponSwitchGizmo()
|
||||
{
|
||||
// 检查Pawn是否属于玩家派系
|
||||
if (MechPawn?.Faction != Faction.OfPlayer)
|
||||
try
|
||||
{
|
||||
return null; // 非玩家派系时不显示
|
||||
// 检查Pawn是否属于玩家派系
|
||||
if (MechPawn?.Faction != Faction.OfPlayer)
|
||||
{
|
||||
return null; // 非玩家派系时不显示
|
||||
}
|
||||
// 检查Pawn是否有效
|
||||
if (MechPawn == null || MechPawn.Dead || MechPawn.Destroyed)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// 检查equipment是否有效
|
||||
if (MechPawn.equipment == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
Command_Action switchWeaponCommand = new Command_Action
|
||||
{
|
||||
defaultLabel = "WULA_SwitchWeapon".Translate(),
|
||||
defaultDesc = "WULA_SwitchWeapon_Desc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("Wula/UI/Abilities/WULA_WeaponSwitchAbility", false) ?? BaseContent.BadTex,
|
||||
action = SwitchWeapon,
|
||||
};
|
||||
// 确保Command不为null
|
||||
if (switchWeaponCommand == null)
|
||||
{
|
||||
Log.Error($"Failed to create weapon switch gizmo for {MechPawn?.LabelCap}");
|
||||
return null;
|
||||
}
|
||||
return switchWeaponCommand;
|
||||
}
|
||||
|
||||
Command_Action switchWeaponCommand = new Command_Action
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
defaultLabel = "WULA_SwitchWeapon".Translate(),
|
||||
defaultDesc = "WULA_SwitchWeapon_Desc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("Wula/UI/Abilities/WULA_WeaponSwitchAbility", false) ?? BaseContent.BadTex,
|
||||
action = SwitchWeapon
|
||||
};
|
||||
return switchWeaponCommand;
|
||||
Log.Error($"Error creating weapon switch gizmo: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -20,6 +20,9 @@ namespace WulaFallenEmpire
|
||||
// 新增:记录最后一次检查敌人的时间
|
||||
private int lastEnemyCheckTick = -99999;
|
||||
|
||||
// 新增:记录最后一次发信的时间
|
||||
private int lastLetterTick = -99999;
|
||||
|
||||
public HediffDef GetTargetInvisibilityDef()
|
||||
{
|
||||
return Props.InvisibilityDef;
|
||||
@@ -49,6 +52,7 @@ namespace WulaFallenEmpire
|
||||
Scribe_Values.Look(ref lastDetectedTick, "lastDetectedTick", 0);
|
||||
Scribe_Values.Look(ref lastRevealedTick, "lastRevealedTick", 0);
|
||||
Scribe_Values.Look(ref lastEnemyCheckTick, "lastEnemyCheckTick", 0);
|
||||
Scribe_Values.Look(ref lastLetterTick, "lastLetterTick", 0);
|
||||
}
|
||||
|
||||
public override void CompTick()
|
||||
@@ -78,6 +82,9 @@ namespace WulaFallenEmpire
|
||||
|
||||
Invisibility.BecomeVisible();
|
||||
lastDetectedTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 触发显现事件
|
||||
TrySendLetter("attack");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -85,7 +92,6 @@ namespace WulaFallenEmpire
|
||||
/// </summary>
|
||||
private void CheckForEnemiesInSight()
|
||||
{
|
||||
|
||||
// 检查频率:每30 tick检查一次(约0.5秒)
|
||||
if (!Sightstealer.IsHashIntervalTick(30) ||
|
||||
Find.TickManager.TicksGame <= lastEnemyCheckTick + 30)
|
||||
@@ -95,12 +101,6 @@ namespace WulaFallenEmpire
|
||||
|
||||
lastEnemyCheckTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 如果配置为只在战斗状态时检查,且当前不在战斗状态,则跳过
|
||||
if (Props.onlyCheckInCombat && !IsInCombatState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查视线内是否有敌人
|
||||
bool enemyInSight = false;
|
||||
List<Pawn> enemiesInSight = new List<Pawn>();
|
||||
@@ -156,108 +156,110 @@ namespace WulaFallenEmpire
|
||||
lastDetectedTick = Find.TickManager.TicksGame;
|
||||
lastRevealedTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 可选:添加视觉或声音效果
|
||||
if (Props.showRevealEffect)
|
||||
// 触发显现事件
|
||||
TrySendLetter("detected", enemiesInSight);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试发送信件
|
||||
/// </summary>
|
||||
private void TrySendLetter(string cause, List<Pawn> enemies = null)
|
||||
{
|
||||
// 检查是否应该发送信件
|
||||
if (!ShouldSendLetter())
|
||||
return;
|
||||
|
||||
// 发送信件
|
||||
SendLetter(cause, enemies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否应该发送信件
|
||||
/// </summary>
|
||||
private bool ShouldSendLetter()
|
||||
{
|
||||
// 如果配置为不发信,直接返回false
|
||||
if (!Props.sendLetterOnReveal)
|
||||
return false;
|
||||
|
||||
// 检查发送间隔
|
||||
int currentTick = Find.TickManager.TicksGame;
|
||||
if (currentTick < lastLetterTick + Props.letterIntervalTicks)
|
||||
{
|
||||
// 还没到发送间隔
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查Pawn是否非玩家控制
|
||||
if (Sightstealer.Faction == Faction.OfPlayer)
|
||||
{
|
||||
// 玩家控制的Pawn,不发送信件
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送信件
|
||||
/// </summary>
|
||||
private void SendLetter(string cause, List<Pawn> enemies = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
int currentTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 获取信件标题和内容
|
||||
string title = Props.letterTitle;
|
||||
string text = Props.letterText;
|
||||
|
||||
// 如果标题或内容为空,使用默认值
|
||||
if (string.IsNullOrEmpty(title))
|
||||
title = "隐身单位现身";
|
||||
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
ShowRevealEffect(enemiesInSight);
|
||||
string enemyInfo = "";
|
||||
if (enemies != null && enemies.Count > 0)
|
||||
{
|
||||
if (enemies.Count == 1)
|
||||
{
|
||||
enemyInfo = $"被 {enemies[0].LabelCap} 发现";
|
||||
}
|
||||
else
|
||||
{
|
||||
enemyInfo = $"被 {enemies.Count} 个敌人发现";
|
||||
}
|
||||
}
|
||||
else if (cause == "attack")
|
||||
{
|
||||
enemyInfo = "发动了攻击";
|
||||
}
|
||||
|
||||
text = $"{Sightstealer.LabelCap}({Sightstealer.Faction?.Name ?? "未知派系"})在 {Sightstealer.Map?.Parent?.LabelCap ?? "未知位置"} 现身了。\n\n{enemyInfo}\n位置:{Sightstealer.Position}";
|
||||
}
|
||||
|
||||
// 可选:发送消息
|
||||
if (Props.sendRevealMessage && Sightstealer.Faction == Faction.OfPlayer)
|
||||
{
|
||||
SendRevealMessage(enemiesInSight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否处于战斗状态
|
||||
/// </summary>
|
||||
private bool IsInCombatState()
|
||||
{
|
||||
// 如果有当前工作且是战斗相关工作
|
||||
if (Sightstealer.CurJob != null)
|
||||
{
|
||||
JobDef jobDef = Sightstealer.CurJob.def;
|
||||
if (jobDef == JobDefOf.AttackMelee ||
|
||||
jobDef == JobDefOf.AttackStatic ||
|
||||
jobDef == JobDefOf.Wait_Combat ||
|
||||
jobDef == JobDefOf.Flee ||
|
||||
jobDef == JobDefOf.FleeAndCower)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有敌人目标
|
||||
if (Sightstealer.mindState.enemyTarget != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果最近受到过伤害
|
||||
if (Find.TickManager.TicksGame - Sightstealer.mindState.lastHarmTick < 300) // 最近5秒内受到伤害
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果最近攻击过目标
|
||||
if (Find.TickManager.TicksGame - Sightstealer.mindState.lastAttackTargetTick < 300)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 显示解除隐身的效果
|
||||
/// </summary>
|
||||
private void ShowRevealEffect(List<Pawn> enemies)
|
||||
{
|
||||
if (Sightstealer.Map == null) return;
|
||||
|
||||
// 创建一个闪光效果
|
||||
FleckMaker.ThrowLightningGlow(Sightstealer.Position.ToVector3Shifted(),
|
||||
Sightstealer.Map, 2f);
|
||||
|
||||
// 可选:播放声音
|
||||
if (Props.revealSound != null)
|
||||
{
|
||||
Props.revealSound.PlayOneShot(new TargetInfo(Sightstealer.Position, Sightstealer.Map));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送解除隐身消息
|
||||
/// </summary>
|
||||
private void SendRevealMessage(List<Pawn> enemies)
|
||||
{
|
||||
if (enemies.Count == 0) return;
|
||||
|
||||
string message;
|
||||
if (enemies.Count == 1)
|
||||
{
|
||||
message = "WFE.RevealedBySingleEnemy".Translate(
|
||||
Sightstealer.LabelShort,
|
||||
enemies[0].LabelShort
|
||||
// 发送信件
|
||||
Letter letter = LetterMaker.MakeLetter(
|
||||
title,
|
||||
text,
|
||||
LetterDefOf.NeutralEvent,
|
||||
new LookTargets(Sightstealer)
|
||||
);
|
||||
|
||||
Find.LetterStack.ReceiveLetter(letter);
|
||||
|
||||
// 更新最后发信时间
|
||||
lastLetterTick = currentTick;
|
||||
}
|
||||
else
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
message = "WFE.RevealedByMultipleEnemies".Translate(
|
||||
Sightstealer.LabelShort,
|
||||
enemies.Count
|
||||
);
|
||||
Log.Error($"CompFighterInvisible: Error sending letter for {Sightstealer?.LabelCap}: {ex}");
|
||||
}
|
||||
|
||||
Messages.Message(message, Sightstealer, MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取下次可以隐身的时间
|
||||
/// </summary>
|
||||
public int NextInvisibilityTick => lastDetectedTick + Props.stealthCooldownTicks;
|
||||
// ... 其他方法保持不变 ...
|
||||
|
||||
/// <summary>
|
||||
/// 手动触发解除隐身(供外部调用)
|
||||
@@ -269,6 +271,11 @@ namespace WulaFallenEmpire
|
||||
Invisibility.BecomeVisible();
|
||||
lastDetectedTick = Find.TickManager.TicksGame;
|
||||
lastRevealedTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 尝试发送信件
|
||||
TrySendLetter("manual");
|
||||
}
|
||||
|
||||
// ... 其他方法保持不变 ...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,24 +46,31 @@ namespace WulaFallenEmpire
|
||||
// 新增:是否忽略某些状态的敌人(如倒地、死亡等)
|
||||
public bool ignoreDownedEnemies = true;
|
||||
public bool ignoreSleepingEnemies = false;
|
||||
|
||||
|
||||
|
||||
// 新增:简化发信配置
|
||||
public bool sendLetterOnReveal = false;
|
||||
public string letterTitle = "";
|
||||
public string letterText = "";
|
||||
public int letterIntervalTicks = 1200;
|
||||
|
||||
public CompProperties_FighterInvisible()
|
||||
{
|
||||
compClass = typeof(CompFighterInvisible);
|
||||
}
|
||||
|
||||
|
||||
public override IEnumerable<string> ConfigErrors(ThingDef parentDef)
|
||||
{
|
||||
foreach (string error in base.ConfigErrors(parentDef))
|
||||
{
|
||||
yield return error;
|
||||
}
|
||||
|
||||
|
||||
if (InvisibilityDef == null)
|
||||
{
|
||||
yield return "InvisibilityDef is not defined for CompProperties_FighterInvisible";
|
||||
}
|
||||
|
||||
|
||||
if (revealDetectionRadius <= 0)
|
||||
{
|
||||
revealDetectionRadius = FirstDetectedRadius;
|
||||
|
||||
@@ -0,0 +1,183 @@
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using Verse.Sound;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class PawnsArrivalModeWorker_EdgeTeleport : PawnsArrivalModeWorker
|
||||
{
|
||||
public override void Arrive(List<Pawn> pawns, IncidentParms parms)
|
||||
{
|
||||
Map map = (Map)parms.target;
|
||||
|
||||
// 如果没有指定生成中心,则使用EdgeDrop的查找方式
|
||||
if (!parms.spawnCenter.IsValid)
|
||||
{
|
||||
parms.spawnCenter = DropCellFinder.FindRaidDropCenterDistant(map);
|
||||
}
|
||||
|
||||
// 为每个Pawn分配一个传送位置(在生成中心附近)
|
||||
foreach (Pawn pawn in pawns)
|
||||
{
|
||||
if (pawn == null || pawn.Dead || pawn.Destroyed)
|
||||
continue;
|
||||
|
||||
// 找到可用的传送位置
|
||||
IntVec3 teleportPos = FindTeleportPosition(map, parms.spawnCenter);
|
||||
|
||||
// 如果Pawn已经在其他地图,需要先将其移到当前地图
|
||||
if (pawn.Map != map)
|
||||
{
|
||||
// 确保Pawn不在任何地图中
|
||||
if (pawn.Spawned)
|
||||
{
|
||||
pawn.DeSpawn();
|
||||
}
|
||||
|
||||
// 将Pawn放入当前地图
|
||||
GenSpawn.Spawn(pawn, teleportPos, map, parms.spawnRotation);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果已经在当前地图,直接移动位置
|
||||
pawn.Position = teleportPos;
|
||||
pawn.Notify_Teleported(true, false);
|
||||
}
|
||||
|
||||
// 播放传送效果
|
||||
PlayTeleportEffect(pawn, teleportPos, map);
|
||||
|
||||
// 确保Pawn有适当的状态
|
||||
EnsurePawnStateAfterTeleport(pawn);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool TryResolveRaidSpawnCenter(IncidentParms parms)
|
||||
{
|
||||
Map map = (Map)parms.target;
|
||||
|
||||
// 与EdgeDrop相同的方式解析生成中心
|
||||
if (!parms.spawnCenter.IsValid)
|
||||
{
|
||||
parms.spawnCenter = DropCellFinder.FindRaidDropCenterDistant(map);
|
||||
}
|
||||
|
||||
parms.spawnRotation = Rot4.Random;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 找到可用的传送位置
|
||||
/// </summary>
|
||||
private IntVec3 FindTeleportPosition(Map map, IntVec3 center)
|
||||
{
|
||||
// 在中心点附近寻找可用的单元格
|
||||
// 我们使用与EdgeWalkInGroups类似的逻辑,但立即传送
|
||||
if (CellFinder.TryFindRandomCellNear(center, map, 10,
|
||||
c => c.Standable(map) &&
|
||||
!c.Fogged(map) &&
|
||||
c.GetRoof(map) != RoofDefOf.RoofRockThick && // 排除厚岩顶
|
||||
map.reachability.CanReachColony(c),
|
||||
out IntVec3 result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 如果找不到合适的单元格,使用备选方案
|
||||
if (RCellFinder.TryFindRandomPawnEntryCell(out result, map, CellFinder.EdgeRoadChance_Hostile))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
// 最后的手段:使用中心点
|
||||
return center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放传送效果
|
||||
/// </summary>
|
||||
private void PlayTeleportEffect(Pawn pawn, IntVec3 pos, Map map)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 播放Skip_ExitNoDelay效果
|
||||
EffecterDef teleportEffect = DefDatabase<EffecterDef>.GetNamed("Skip_ExitNoDelay");
|
||||
if (teleportEffect != null)
|
||||
{
|
||||
Effecter effecter = teleportEffect.Spawn();
|
||||
effecter.ticksLeft = 30; // 设置效果持续时间
|
||||
effecter.Trigger(new TargetInfo(pos, map), new TargetInfo(pos, map));
|
||||
effecter.Cleanup();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果找不到指定的效果,使用跳跃效果
|
||||
EffecterDef jumpEffect = EffecterDefOf.Skip_Exit;
|
||||
if (jumpEffect != null)
|
||||
{
|
||||
Effecter effecter = jumpEffect.Spawn();
|
||||
effecter.ticksLeft = 30;
|
||||
effecter.Trigger(new TargetInfo(pos, map), new TargetInfo(pos, map));
|
||||
effecter.Cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// 可选:播放声音效果
|
||||
SoundDef sound = SoundDefOf.PsychicPulseGlobal;
|
||||
if (sound != null)
|
||||
{
|
||||
sound.PlayOneShot(new TargetInfo(pos, map));
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Warning($"Failed to play teleport effect for {pawn?.LabelCap}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保Pawn传送后的状态
|
||||
/// </summary>
|
||||
private void EnsurePawnStateAfterTeleport(Pawn pawn)
|
||||
{
|
||||
if (pawn == null)
|
||||
return;
|
||||
|
||||
// 重置当前工作
|
||||
if (pawn.CurJob != null)
|
||||
{
|
||||
pawn.jobs.StopAll();
|
||||
}
|
||||
|
||||
// 如果是殖民者或友军,设置为等待战斗状态
|
||||
if (pawn.Faction == Faction.OfPlayer || !pawn.HostileTo(Faction.OfPlayer))
|
||||
{
|
||||
pawn.jobs.StartJob(new Job(JobDefOf.Wait_Combat, 600, true),
|
||||
JobCondition.InterruptForced, null, false, true, null, null, false);
|
||||
}
|
||||
|
||||
// 重置心理状态
|
||||
if (pawn.mindState != null)
|
||||
{
|
||||
pawn.mindState.enemyTarget = null;
|
||||
pawn.mindState.mentalStateHandler?.Reset();
|
||||
}
|
||||
|
||||
// 如果是机械族,可能需要特殊处理
|
||||
if (pawn.RaceProps.IsMechanoid)
|
||||
{
|
||||
var comp = pawn.GetComp<CompAutonomousMech>();
|
||||
if (comp != null)
|
||||
{
|
||||
// 如果是自主机械,确保进入适当状态
|
||||
if (!pawn.Drafted && pawn.Faction == Faction.OfPlayer)
|
||||
{
|
||||
pawn.drafter.Drafted = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,9 +53,6 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
pawn.drafter.Drafted = true;
|
||||
}
|
||||
|
||||
// 发送生成消息
|
||||
Messages.Message("SkyfallerPawnLanded".Translate(pawn.LabelShortCap), pawn, MessageTypeDefOf.NeutralEvent);
|
||||
}
|
||||
|
||||
private Faction GetFaction()
|
||||
|
||||
@@ -250,6 +250,7 @@
|
||||
<Compile Include="HediffComp\WULA_HediffSpawner\Tools.cs" />
|
||||
<Compile Include="Job\JobDriver_InspectBuilding.cs" />
|
||||
<Compile Include="Job\JobGiver_InspectBuilding.cs" />
|
||||
<Compile Include="PawnsArrivalMode\PawnsArrivalModeWorker_EdgeTeleport.cs" />
|
||||
<Compile Include="Pawn\Comp_MultiTurretGun.cs" />
|
||||
<Compile Include="Pawn\Comp_PawnRenderExtra.cs" />
|
||||
<Compile Include="Pawn\WULA_AutoMechCarrier\CompAutoMechCarrier.cs" />
|
||||
|
||||
Reference in New Issue
Block a user