This commit is contained in:
2025-11-21 15:43:54 +08:00
parent 2ea2c33059
commit a0ee1164bf
10 changed files with 352 additions and 75 deletions

View File

@@ -246,7 +246,7 @@
<AbilityDef>
<defName>WULA_Psi_Skip</defName>
<label>乌拉帝国术式:灵能跃迁</label>
<description>使用灵能撕开一个连接自身、虚境和目的地的单向通道,随后以极短的时间穿过整个通道,以瞬间移动到目标地点</description>
<description>使用灵能撕开一个连接自身、虚境和目的地的单向通道,随后以极短的时间穿过整个通道,以瞬间移动到目标地点</description>
<iconPath>UI/Abilities/Skip</iconPath>
<writeCombatLog>True</writeCombatLog>
<showPsycastEffects>False</showPsycastEffects>
@@ -401,7 +401,6 @@
<cooldownTicksRange>600</cooldownTicksRange>
<warmupEffecter>HoraxianSpellLight_Warmup</warmupEffecter>
<statBases>
<Ability_EffectRadius>8</Ability_EffectRadius>
<Ability_Duration>600</Ability_Duration>
</statBases>
<verbProperties>
@@ -414,9 +413,10 @@
</targetParams>
</verbProperties>
<comps>
<li Class="CompProperties_AbilitySpawn">
<li Class="WulaFallenEmpire.CompProperties_AbilitySpawnAligned">
<thingDef>WULA_Black_Hole_Entity</thingDef>
<allowOnBuildings>false</allowOnBuildings>
<alignFaction>false</alignFaction>
</li>
</comps>
</AbilityDef>
@@ -444,9 +444,8 @@
<scaleWithPsychicSensitivity>true</scaleWithPsychicSensitivity>
<minDamageFactor>0.5</minDamageFactor>
<maxDamageFactor>5</maxDamageFactor>
<affectFriendly>false</affectFriendly>
<affectHostile>true</affectHostile>
<affectBuildings>true</affectBuildings>
<affectEverything>true</affectEverything>
<ignoreFactionRelations>true</ignoreFactionRelations>
</li>
</comps>
</ThingDef>

View File

@@ -748,4 +748,64 @@
<li>Placeworker_AttachedToWall</li>
</placeWorkers>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>WULA_BroadshieldProjector</defName>
<label>burnout low-shield</label>
<description>A man-portable low-angle shield projector. Bullets can go out, but not in. Its compact power source burns out after a few seconds of use.</description>
<altitudeLayer>BuildingOnTop</altitudeLayer>
<drawerType>MapMeshAndRealTime</drawerType>
<pathCost>0</pathCost>
<passability>Standable</passability>
<fillPercent>0.35</fillPercent>
<graphicData>
<texPath>Things/Building/Security/BroadshieldProjector</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>0.7</drawSize>
<shadowData>
<volume>(0.3, 0.2, 0.3)</volume>
<offset>(0,0,-0.1)</offset>
</shadowData>
<damageData>
<rect>(0.2,0.2,0.6,0.6)</rect>
</damageData>
</graphicData>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<Flammability>0.5</Flammability>
</statBases>
<tickerType>Normal</tickerType>
<killedLeavings/>
<rotatable>false</rotatable>
<selectable>true</selectable>
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
<soundImpactDefault>BulletImpact_Metal</soundImpactDefault>
<repairEffect>ConstructMetal</repairEffect>
<resourcesFractionWhenDeconstructed>0</resourcesFractionWhenDeconstructed>
<building>
<claimable>false</claimable>
<destroySound>BuildingDestroyed_Metal_Small</destroySound>
<ai_chillDestination>false</ai_chillDestination>
<isEdifice>false</isEdifice>
<destroyEffecter>Broadshield_Burnout</destroyEffecter>
</building>
<comps>
<li Class="WulaFallenEmpire.CompProperties_PeriodicGameCondition">
<gameConditionDef>SolarFlare</gameConditionDef>
<intervalDays>1</intervalDays>
<durationDays>0.01</durationDays>
</li>
<li Class="CompProperties_ProjectileInterceptor">
<radius>4.9</radius>
<interceptGroundProjectiles>true</interceptGroundProjectiles>
<interceptNonHostileProjectiles>true</interceptNonHostileProjectiles>
<interceptOutgoingProjectiles>false</interceptOutgoingProjectiles>
<color>(0.6, 0.6, 0.8)</color>
<idlePulseSpeed>3</idlePulseSpeed>
<minIdleAlpha>0.05</minIdleAlpha>
<disarmedByEmpForTicks>600</disarmedByEmpForTicks>
<activeSound>Broadshield_Ambience</activeSound>
</li>
</comps>
</ThingDef>
</Defs>

View File

@@ -1352,6 +1352,17 @@
<flyingAnimationEast>WULA_Hover_FlyEast</flyingAnimationEast>
<flyingAnimationSouth>WULA_Hover_FlySouth</flyingAnimationSouth>
</li>
<li Class="WulaFallenEmpire.CompProperties_AreaDamage">
<radius>3.5</radius>
<damageIntervalTicks>30</damageIntervalTicks>
<damageDef>Crush</damageDef>
<damageAmount>4</damageAmount>
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
<affectFriendly>false</affectFriendly>
<affectNeutral>false</affectNeutral>
<affectHostile>true</affectHostile>
<ignoreFactionRelations>true</ignoreFactionRelations>
</li>
</comps>
</ThingDef>
<ThingDef Name="WULA_Mech_Flyer" ParentName="BaseMechanoidWalker">

View File

@@ -34,9 +34,6 @@ namespace WulaFallenEmpire
{
ticksUntilNextSpawn = Props.spawnIntervalTicks;
}
Log.Message($"FlyOver Escort initialized: {Props.spawnIntervalTicks} ticks interval, max {Props.maxEscorts} escorts");
Log.Message($"Safe distances - From Main: {Props.minSafeDistanceFromMain}, Between Escorts: {Props.minSafeDistanceBetweenEscorts}");
}
public override void CompTick()
@@ -50,7 +47,6 @@ namespace WulaFallenEmpire
if (!hasInitialized && mainFlyOver.hasStarted)
{
hasInitialized = true;
Log.Message($"FlyOver Escort: Main FlyOver started at {mainFlyOver.startPosition}");
}
// 清理已销毁的伴飞
@@ -136,14 +132,11 @@ namespace WulaFallenEmpire
escortDataByID[escortID] = visualData;
successfulSpawns++;
Log.Message($"Spawned escort #{successfulSpawns} for FlyOver at {mainFlyOver.DrawPos}, scale: {visualData.scale:F2}, maskAlpha: {visualData.heightMaskAlpha:F2}");
}
else
{
// 不安全,销毁这个伴飞
escort.Destroy();
Log.Message($"Escort spawn attempt {attempt + 1}: Position too close to existing escort, trying again");
}
}
@@ -169,7 +162,6 @@ namespace WulaFallenEmpire
float distToMain = Vector3.Distance(newPos, mainFlyOver.DrawPos);
if (distToMain < Props.minSafeDistanceFromMain)
{
Log.Message($"Escort too close to main FlyOver: {distToMain:F1} < {Props.minSafeDistanceFromMain}");
return false;
}
}
@@ -185,7 +177,6 @@ namespace WulaFallenEmpire
float distToEscort = Vector3.Distance(newPos, existingEscort.DrawPos);
if (distToEscort < Props.minSafeDistanceBetweenEscorts)
{
Log.Message($"Escort too close to existing escort: {distToEscort:F1} < {Props.minSafeDistanceBetweenEscorts}");
return false;
}
}
@@ -252,8 +243,6 @@ namespace WulaFallenEmpire
// 设置伴飞属性 - 现在传入 visualData
SetupEscortProperties(escort, mainFlyOver, visualData);
Log.Message($"Created escort: {escortStart} -> {escortEnd}, speed: {escortSpeed}, altitude: {escortAltitude}");
return escort;
}
catch (System.Exception ex)
@@ -371,8 +360,6 @@ namespace WulaFallenEmpire
{
escort.playFlyOverSound = false;
}
Log.Message($"Set escort properties: scale={visualData.scale:F2}, isEscort={escort.isEscort}");
}
private void UpdateEscortPositions(FlyOver mainFlyOver)
@@ -532,8 +519,6 @@ namespace WulaFallenEmpire
escortDataByID[escortIDs[i]] = escortDataList[i];
}
}
Log.Message($"Loaded escort visual data: {escortDataByID.Count} entries");
}
else if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
@@ -560,8 +545,6 @@ namespace WulaFallenEmpire
// 重新设置伴飞的缩放
escort.escortScale = visualData.scale;
Log.Message($"Rebuilt visual data for escort {escortID}: scale={visualData.scale:F2}");
}
else
{
@@ -569,13 +552,9 @@ namespace WulaFallenEmpire
var newVisualData = GenerateEscortVisualData();
escortVisualData[escort] = newVisualData;
escort.escortScale = newVisualData.scale;
Log.Message($"Regenerated visual data for escort {escortID}: scale={newVisualData.scale:F2}");
}
}
}
Log.Message($"Rebuilt escort visual data: {escortVisualData.Count} escorts");
}
// 公共方法:强制生成伴飞

View File

@@ -64,45 +64,74 @@ namespace WulaFallenEmpire
private bool IsValidTarget(Thing thing)
{
// 检查是否有生命值
if (thing.def.useHitPoints == false || thing.HitPoints <= 0)
return false;
// 检查物体类型过滤
if (thing is Building && !Props.affectBuildings)
return false;
if (thing is Pawn && !Props.affectPawns)
return false;
if (thing is Plant && !Props.affectPlants)
return false;
// 检查阵营关系(如果是生物)
// 检查是否为 PawnPawn 有独立的健康系统)
if (thing is Pawn pawn)
{
Faction targetFaction = pawn.Faction;
Faction parentFaction = parent.Faction;
if (pawn.Dead || pawn.Downed)
return false;
if (targetFaction == null)
// 检查是否影响生物
if (!Props.affectPawns)
return false;
// 如果父物体没有派系,则只检查目标派系
if (parentFaction == null)
{
if (!Props.affectNeutral)
if (targetFaction == null && !Props.affectNeutral)
return false;
}
else if (targetFaction == parentFaction)
{
if (!Props.affectFriendly)
if (targetFaction != null && targetFaction.IsPlayer && !Props.affectFriendly)
return false;
}
else if (targetFaction.HostileTo(parentFaction))
{
if (!Props.affectHostile)
if (targetFaction != null && !targetFaction.IsPlayer && !Props.affectHostile)
return false;
}
else
{
if (!Props.affectNeutral)
return false;
// 正常阵营关系检查
if (targetFaction == null)
{
if (!Props.affectNeutral)
return false;
}
else if (targetFaction == parentFaction)
{
if (!Props.affectFriendly)
return false;
}
else if (targetFaction.HostileTo(parentFaction))
{
if (!Props.affectHostile)
return false;
}
else
{
if (!Props.affectNeutral)
return false;
}
}
}
else
{
// 对于非 Pawn 物体,检查生命值系统
if (thing.def.useHitPoints == false || thing.HitPoints <= 0)
return false;
// 检查物体类型过滤
if (thing is Building && !Props.affectBuildings)
return false;
if (thing is Plant && !Props.affectPlants)
return false;
}
// 如果设置为影响所有物体,跳过后续检查
if (Props.affectEverything)
return true;
// 如果忽略阵营关系,跳过阵营检查
if (Props.ignoreFactionRelations)
return true;
return true;
}
@@ -119,7 +148,7 @@ namespace WulaFallenEmpire
DamageInfo damageInfo = new DamageInfo(
Props.damageDef,
finalDamageAmount,
armorPenetration: 0f,
armorPenetration: Props.armorPenetration,
instigator: parent,
hitPart: null,
weapon: null,
@@ -128,6 +157,9 @@ namespace WulaFallenEmpire
// 应用伤害
target.TakeDamage(damageInfo);
// 特殊效果处理
HandleSpecialEffects(target, damageInfo);
}
/// <summary>
@@ -172,35 +204,29 @@ namespace WulaFallenEmpire
}
// 返回心灵敏感度作为伤害倍率
// 例如敏感度0.5 = 0.5倍伤害敏感度1.5 = 1.5倍伤害
return psychicSensitivity;
}
/// <summary>
/// 处理特殊效果(如伤害类型特定的效果)
/// </summary>
private void HandleSpecialEffects(Thing target, DamageInfo damageInfo)
{
// 如果是 Pawn可以添加额外的效果
if (target is Pawn pawn)
{
// 显示伤害数值(调试用)
if (Props.showDamageNumbers)
{
MoteMaker.ThrowText(target.DrawPos, target.Map, damageInfo.Amount.ToString());
}
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref ticksUntilNextDamage, "ticksUntilNextDamage", Props.damageIntervalTicks);
}
/// <summary>
/// 调试方法:显示伤害计算信息
/// </summary>
public string GetDamageDebugInfo(Thing target)
{
if (target is Pawn pawn)
{
float psychicSensitivity = pawn.GetStatValue(StatDefOf.PsychicSensitivity);
float damageFactor = CalculatePsychicSensitivityFactor(pawn);
int finalDamage = CalculateFinalDamage(target);
return $"目标: {pawn.Label}\n" +
$"心灵敏感度: {psychicSensitivity:F2}\n" +
$"伤害倍率: {damageFactor:F2}\n" +
$"基础伤害: {Props.damageAmount}\n" +
$"最终伤害: {finalDamage}";
}
return $"目标: {target.Label}\n基础伤害: {Props.damageAmount}";
}
}
}

View File

@@ -9,6 +9,7 @@ namespace WulaFallenEmpire
public int damageIntervalTicks = 60; // B: 伤害间隔(帧数)
public DamageDef damageDef; // C: 伤害类型
public int damageAmount = 10; // 基础伤害量
public float armorPenetration = 0f; // 护甲穿透
// 伤害缩放设置
public bool scaleWithPsychicSensitivity = false; // 是否随心灵敏感度缩放
@@ -24,6 +25,13 @@ namespace WulaFallenEmpire
public bool affectBuildings = true; // 是否影响建筑
public bool affectPawns = true; // 是否影响生物
public bool affectPlants = false; // 是否影响植物
// 特殊设置
public bool ignoreFactionRelations = false; // 忽略所有阵营关系检查(用于无派系实体)
public bool affectEverything = false; // 影响范围内所有有生命值的物体
// 特殊效果
public bool showDamageNumbers = false; // 显示伤害数值(调试用)
public CompProperties_AreaDamage()
{

View File

@@ -0,0 +1,162 @@
using RimWorld;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class CompPeriodicGameCondition : ThingComp
{
private int ticksUntilNextCondition;
public CompProperties_PeriodicGameCondition Props => (CompProperties_PeriodicGameCondition)props;
public override void Initialize(CompProperties props)
{
base.Initialize(props);
ticksUntilNextCondition = GetIntervalTicks();
}
public override void CompTick()
{
base.CompTick();
if (!parent.Spawned || parent.Map == null)
return;
ticksUntilNextCondition--;
if (ticksUntilNextCondition <= 0)
{
TryMakeGameCondition();
ticksUntilNextCondition = GetIntervalTicks();
}
}
private int GetIntervalTicks()
{
return Mathf.RoundToInt(Props.intervalDays * 60000f); // 1天 = 60000 ticks
}
private int GetDurationTicks()
{
return Mathf.RoundToInt(Props.durationDays * 60000f);
}
private void TryMakeGameCondition()
{
Map map = parent.Map;
if (map == null)
return;
// 检查是否可以触发
if (!CanMakeGameCondition(map))
return;
// 创建游戏条件
GameCondition condition = GameConditionMaker.MakeCondition(Props.gameConditionDef, GetDurationTicks());
map.gameConditionManager.RegisterCondition(condition);
// 发送通知
if (Props.sendLetter)
{
TrySendLetter(map, condition);
}
}
private bool CanMakeGameCondition(Map map)
{
if (Props.gameConditionDef == null)
return false;
GameConditionManager conditionManager = map.gameConditionManager;
if (conditionManager == null)
return false;
// 检查是否被其他条件阻止
if (Props.checkPreventIncidents)
{
foreach (GameCondition activeCondition in conditionManager.ActiveConditions)
{
if (activeCondition.def.preventIncidents)
{
return false;
}
}
}
// 检查是否已存在相同条件
if (Props.checkAlreadyActive && conditionManager.ConditionIsActive(Props.gameConditionDef))
{
return false;
}
// 检查是否可以共存
if (Props.checkCanCoexist)
{
foreach (GameCondition activeCondition in conditionManager.ActiveConditions)
{
if (!Props.gameConditionDef.CanCoexistWith(activeCondition.def))
{
return false;
}
}
}
// 特殊条件:检查水域是否有鱼
if (Props.requireFish && ModsConfig.OdysseyActive)
{
if (map.waterBodyTracker == null || !map.waterBodyTracker.AnyBodyContainsFish)
{
return false;
}
}
return true;
}
private void TrySendLetter(Map map, GameCondition condition)
{
if (condition.HiddenByOtherCondition(map))
return;
// 检查是否有地图会受到这个条件影响
bool anyMapAffected = false;
foreach (Map currentMap in Find.Maps)
{
if (condition.CanApplyOnMap(currentMap))
{
anyMapAffected = true;
break;
}
}
if (!anyMapAffected)
return;
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref ticksUntilNextCondition, "ticksUntilNextCondition", GetIntervalTicks());
}
/// <summary>
/// 手动触发一次游戏条件(用于调试或特殊事件)
/// </summary>
public void TriggerNow()
{
ticksUntilNextCondition = 0;
}
/// <summary>
/// 获取下一次触发的时间信息
/// </summary>
public string GetNextTriggerInfo()
{
if (!parent.Spawned)
return "Not spawned";
float daysUntilNext = ticksUntilNextCondition / 60000f;
return $"Next trigger in: {daysUntilNext:F1} days";
}
}
}

View File

@@ -0,0 +1,30 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_PeriodicGameCondition : CompProperties
{
public GameConditionDef gameConditionDef; // 要创建的游戏条件
public float intervalDays = 1f; // 触发间隔(天)
public float durationDays = 1f; // 游戏条件持续时间(天)
// 条件检查设置
public bool checkCanCoexist = true; // 检查是否与其他条件共存
public bool checkAlreadyActive = true; // 检查是否已存在相同条件
public bool checkPreventIncidents = true; // 检查是否被其他条件阻止
// 通知设置
public bool sendLetter = true; // 是否发送信件通知
public string letterLabel; // 信件标题(如为空则使用游戏条件的默认标题)
public LetterDef letterDef; // 信件类型
// 特殊条件
public bool requireFish = false; // 是否需要水域有鱼仅限Odyssey DLC
public CompProperties_PeriodicGameCondition()
{
compClass = typeof(CompPeriodicGameCondition);
}
}
}

View File

@@ -239,6 +239,8 @@
<Compile Include="ThingComp\WULA_CustomUniqueWeapon\CompProperties_CustomUniqueWeapon.cs" />
<Compile Include="ThingComp\WULA_MechRepairKit\CompUseEffect_FixAllHealthConditions.cs" />
<Compile Include="ThingComp\WULA_MechRepairKit\Recipe_AdministerWulaMechRepairKit.cs" />
<Compile Include="ThingComp\WULA_PeriodicGameCondition\CompPeriodicGameCondition.cs" />
<Compile Include="ThingComp\WULA_PeriodicGameCondition\CompProperties_PeriodicGameCondition.cs" />
<Compile Include="ThingComp\WULA_PersonaCore\CompExperienceCore.cs" />
<Compile Include="ThingComp\WULA_PersonaCore\CompExperienceDataPack.cs" />
<Compile Include="ThingComp\WULA_PersonaCore\CompProperties_ExperienceCore.cs" />