虫群的自定义袭击
This commit is contained in:
Binary file not shown.
@@ -3,12 +3,8 @@
|
||||
"WorkspaceRootPath": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\",
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\abilities\\compabilityeffect_transformcorpse.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:abilities\\compabilityeffect_transformcorpse.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\building_comps\\comprefuelablenutrition.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:building_comps\\comprefuelablenutrition.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
"AbsoluteMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|d:\\steamlibrary\\steamapps\\common\\rimworld\\mods\\arachnaeswarm\\source\\arachnaeswarm\\storyteller\\incidentworker_customraid.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EAE0DB6B-E282-C812-7F5A-6D13E9D24581}|ArachnaeSwarm.csproj|solutionrelative:storyteller\\incidentworker_customraid.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
}
|
||||
],
|
||||
"DocumentGroupContainers": [
|
||||
@@ -18,35 +14,23 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 2,
|
||||
"SelectedChildIndex": 1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{1c4feeaa-4718-4aa9-859d-94ce25d182ba}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "CompRefuelableNutrition.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompRefuelableNutrition.cs",
|
||||
"RelativeDocumentMoniker": "Building_Comps\\CompRefuelableNutrition.cs",
|
||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Building_Comps\\CompRefuelableNutrition.cs",
|
||||
"RelativeToolTip": "Building_Comps\\CompRefuelableNutrition.cs",
|
||||
"ViewState": "AgIAABAAAAAAAAAAAAAuwBYAAAAhAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-10-15T08:04:45.513Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "CompAbilityEffect_TransformCorpse.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
||||
"RelativeDocumentMoniker": "Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
||||
"RelativeToolTip": "Abilities\\CompAbilityEffect_TransformCorpse.cs",
|
||||
"ViewState": "AgIAAAAAAAAAAAAAAAAAABIAAABCAAAAAAAAAA==",
|
||||
"Title": "IncidentWorker_CustomRaid.cs",
|
||||
"DocumentMoniker": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Storyteller\\IncidentWorker_CustomRaid.cs",
|
||||
"RelativeDocumentMoniker": "Storyteller\\IncidentWorker_CustomRaid.cs",
|
||||
"ToolTip": "D:\\SteamLibrary\\steamapps\\common\\RimWorld\\Mods\\ArachnaeSwarm\\Source\\ArachnaeSwarm\\Storyteller\\IncidentWorker_CustomRaid.cs",
|
||||
"RelativeToolTip": "Storyteller\\IncidentWorker_CustomRaid.cs",
|
||||
"ViewState": "AgIAAAYBAAAAAAAAAAAgwBYBAAAyAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2025-10-15T08:02:12.842Z",
|
||||
"WhenOpened": "2025-10-16T07:14:58.682Z",
|
||||
"EditorCaption": ""
|
||||
}
|
||||
]
|
||||
|
||||
@@ -120,6 +120,12 @@
|
||||
<Compile Include="EventSystem\Letter_EventChoice.cs" />
|
||||
<Compile Include="EventSystem\QuestNode_Root_EventLetter.cs" />
|
||||
<Compile Include="Jobs\JobDriver_CarryPrisonerToRefuelingVat.cs" />
|
||||
<Compile Include="Storyteller\CustomRaidDef.cs" />
|
||||
<Compile Include="Storyteller\CustomRaidTracker.cs" />
|
||||
<Compile Include="Storyteller\IncidentParmsExtensions.cs" />
|
||||
<Compile Include="Storyteller\IncidentWorker_CustomRaid.cs" />
|
||||
<Compile Include="Storyteller\RaidWaveDef.cs" />
|
||||
<Compile Include="Storyteller\RaidWavePoolDef.cs" />
|
||||
<Compile Include="Verbs\Verb_ShootWithOffset.cs" />
|
||||
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompAbilityEffect_AbilityShowTemperatureRange.cs" />
|
||||
<Compile Include="Abilities\ARA_ShowTemperatureRange\CompProperties_AbilityShowTemperatureRange.cs" />
|
||||
|
||||
51
Source/ArachnaeSwarm/Storyteller/CustomRaidDef.cs
Normal file
51
Source/ArachnaeSwarm/Storyteller/CustomRaidDef.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CustomRaidDef : Def
|
||||
{
|
||||
public FactionDef factionDef;
|
||||
public List<PointWavePool> pointWavePools;
|
||||
public int baseRaidNembers;
|
||||
public PointsGrowthPerWave pointsGrowthPerWave;
|
||||
|
||||
public override IEnumerable<string> ConfigErrors()
|
||||
{
|
||||
foreach (string error in base.ConfigErrors())
|
||||
{
|
||||
yield return error;
|
||||
}
|
||||
|
||||
if (factionDef == null)
|
||||
{
|
||||
yield return "factionDef is not defined";
|
||||
}
|
||||
|
||||
if (pointWavePools.NullOrEmpty())
|
||||
{
|
||||
yield return "pointWavePools is empty";
|
||||
}
|
||||
|
||||
if (baseRaidNembers <= 0)
|
||||
{
|
||||
yield return "baseRaidNembers must be positive";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PointWavePool
|
||||
{
|
||||
public float minPoints;
|
||||
public float maxPoints = 99999f; // 默认值表示无上限
|
||||
public RaidWavePoolDef wavePool;
|
||||
}
|
||||
|
||||
public class PointsGrowthPerWave
|
||||
{
|
||||
public string growthType = "Linear"; // Linear/Exponential
|
||||
public float linearGrowth = 1f;
|
||||
public float exponentialBase = 1.15f;
|
||||
}
|
||||
}
|
||||
73
Source/ArachnaeSwarm/Storyteller/CustomRaidTracker.cs
Normal file
73
Source/ArachnaeSwarm/Storyteller/CustomRaidTracker.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CustomRaidTracker : GameComponent
|
||||
{
|
||||
private Dictionary<string, int> waveCounters = new Dictionary<string, int>();
|
||||
|
||||
public CustomRaidTracker(Game game)
|
||||
{
|
||||
// 构造函数
|
||||
}
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Collections.Look(ref waveCounters, "waveCounters", LookMode.Value, LookMode.Value);
|
||||
|
||||
// 如果waveCounters为null(加载旧存档时可能发生),初始化它
|
||||
if (waveCounters == null)
|
||||
{
|
||||
waveCounters = new Dictionary<string, int>();
|
||||
}
|
||||
}
|
||||
|
||||
public int GetCurrentWave(CustomRaidDef raidDef)
|
||||
{
|
||||
if (raidDef == null)
|
||||
{
|
||||
Log.Warning("GetCurrentWave called with null raidDef");
|
||||
return 0;
|
||||
}
|
||||
|
||||
string key = raidDef.defName;
|
||||
if (!waveCounters.ContainsKey(key))
|
||||
{
|
||||
waveCounters[key] = 0;
|
||||
}
|
||||
|
||||
return waveCounters[key];
|
||||
}
|
||||
|
||||
public void IncrementWave(CustomRaidDef raidDef)
|
||||
{
|
||||
if (raidDef != null)
|
||||
{
|
||||
string key = raidDef.defName;
|
||||
int currentWave = GetCurrentWave(raidDef);
|
||||
waveCounters[key] = currentWave + 1;
|
||||
|
||||
Log.Message($"CustomRaidTracker: Incremented wave for {raidDef.defName} to {waveCounters[key]}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning("IncrementWave called with null raidDef");
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetWave(CustomRaidDef raidDef)
|
||||
{
|
||||
if (raidDef != null)
|
||||
{
|
||||
waveCounters[raidDef.defName] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetAllWaves()
|
||||
{
|
||||
waveCounters.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
111
Source/ArachnaeSwarm/Storyteller/IncidentParmsExtensions.cs
Normal file
111
Source/ArachnaeSwarm/Storyteller/IncidentParmsExtensions.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public static class IncidentParmsExtensions
|
||||
{
|
||||
// 使用静态字典来存储自定义数据
|
||||
private static Dictionary<IncidentParms, CustomRaidData> customRaidData = new Dictionary<IncidentParms, CustomRaidData>();
|
||||
|
||||
public class CustomRaidData
|
||||
{
|
||||
public RaidWaveDef WaveDef { get; set; }
|
||||
public int RaidSize { get; set; } = -1;
|
||||
public CustomRaidDef RaidDef { get; set; }
|
||||
public int WaveNumber { get; set; }
|
||||
}
|
||||
|
||||
public static void SetCustomRaidWave(this IncidentParms parms, RaidWaveDef waveDef)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
customRaidData[parms] = new CustomRaidData();
|
||||
|
||||
customRaidData[parms].WaveDef = waveDef;
|
||||
}
|
||||
|
||||
public static RaidWaveDef GetCustomRaidWave(this IncidentParms parms)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
return null;
|
||||
|
||||
return customRaidData[parms].WaveDef;
|
||||
}
|
||||
|
||||
public static void SetCustomRaidSize(this IncidentParms parms, int raidSize)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
customRaidData[parms] = new CustomRaidData();
|
||||
|
||||
customRaidData[parms].RaidSize = raidSize;
|
||||
}
|
||||
|
||||
public static int GetCustomRaidSize(this IncidentParms parms)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
return -1;
|
||||
|
||||
return customRaidData[parms].RaidSize;
|
||||
}
|
||||
|
||||
public static void SetCustomRaidDef(this IncidentParms parms, CustomRaidDef raidDef)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
customRaidData[parms] = new CustomRaidData();
|
||||
|
||||
customRaidData[parms].RaidDef = raidDef;
|
||||
}
|
||||
|
||||
public static CustomRaidDef GetCustomRaidDef(this IncidentParms parms)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
return null;
|
||||
|
||||
return customRaidData[parms].RaidDef;
|
||||
}
|
||||
|
||||
public static void SetCustomRaidWaveNumber(this IncidentParms parms, int waveNumber)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
customRaidData[parms] = new CustomRaidData();
|
||||
|
||||
customRaidData[parms].WaveNumber = waveNumber;
|
||||
}
|
||||
|
||||
public static int GetCustomRaidWaveNumber(this IncidentParms parms)
|
||||
{
|
||||
if (!customRaidData.ContainsKey(parms))
|
||||
return 0;
|
||||
|
||||
return customRaidData[parms].WaveNumber;
|
||||
}
|
||||
|
||||
public static bool IsCustomRaid(this IncidentParms parms)
|
||||
{
|
||||
return parms.GetCustomRaidWave() != null;
|
||||
}
|
||||
|
||||
// 清理方法,在事件完成后调用
|
||||
public static void ClearCustomData(this IncidentParms parms)
|
||||
{
|
||||
if (customRaidData.ContainsKey(parms))
|
||||
customRaidData.Remove(parms);
|
||||
}
|
||||
|
||||
// 批量清理方法,用于清理所有不再使用的 IncidentParms
|
||||
public static void CleanupOrphanedData()
|
||||
{
|
||||
// 这里可以添加逻辑来清理不再使用的 IncidentParms 引用
|
||||
// 例如,如果 IncidentParms 已经被销毁,我们可以从字典中移除
|
||||
// 由于 RimWorld 没有提供弱引用,这个清理可能需要手动触发
|
||||
// 或者定期调用
|
||||
}
|
||||
|
||||
// 获取所有存储的自定义数据(用于调试)
|
||||
public static int GetStoredDataCount()
|
||||
{
|
||||
return customRaidData.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
418
Source/ArachnaeSwarm/Storyteller/IncidentWorker_CustomRaid.cs
Normal file
418
Source/ArachnaeSwarm/Storyteller/IncidentWorker_CustomRaid.cs
Normal file
@@ -0,0 +1,418 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class IncidentWorker_CustomRaid : IncidentWorker_Raid
|
||||
{
|
||||
private CustomRaidTracker GetTracker()
|
||||
{
|
||||
if (Current.ProgramState != ProgramState.Playing) return null;
|
||||
|
||||
Game game = Current.Game;
|
||||
if (game == null) return null;
|
||||
|
||||
CustomRaidTracker tracker = game.GetComponent<CustomRaidTracker>();
|
||||
if (tracker == null)
|
||||
{
|
||||
tracker = new CustomRaidTracker(game);
|
||||
game.components.Add(tracker);
|
||||
}
|
||||
return tracker;
|
||||
}
|
||||
|
||||
protected override bool CanFireNowSub(IncidentParms parms)
|
||||
{
|
||||
// 获取自定义袭击定义
|
||||
CustomRaidDef raidDef = GetCustomRaidDef();
|
||||
if (raidDef == null)
|
||||
{
|
||||
Log.Warning("CustomRaidDef not found in CanFireNowSub");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查最小天数
|
||||
if (GenDate.DaysPassedSinceSettle < 15f) // 可以配置化
|
||||
{
|
||||
Log.Message($"Custom raid cannot fire: only {GenDate.DaysPassedSinceSettle} days passed, need 15");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查目标是否有效
|
||||
if (parms.target == null)
|
||||
{
|
||||
Log.Warning("Custom raid target is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查目标是否有有效的地图
|
||||
Map map = parms.target as Map;
|
||||
if (map == null)
|
||||
{
|
||||
Log.Warning("Custom raid target is not a Map or map is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查派系是否存在
|
||||
Faction faction = Find.FactionManager.FirstFactionOfDef(raidDef.factionDef);
|
||||
if (faction == null)
|
||||
{
|
||||
Log.Warning($"Faction {raidDef.factionDef?.defName} not found for custom raid");
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.CanFireNowSub(parms);
|
||||
}
|
||||
|
||||
protected override bool TryExecuteWorker(IncidentParms parms)
|
||||
{
|
||||
Log.Message("=== Custom Raid Incident Started ===");
|
||||
|
||||
// 检查目标地图
|
||||
Map map = parms.target as Map;
|
||||
if (map == null)
|
||||
{
|
||||
Log.Error("Custom raid target is not a valid Map");
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomRaidDef raidDef = GetCustomRaidDef();
|
||||
if (raidDef == null)
|
||||
{
|
||||
Log.Error("CustomRaidDef not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
CustomRaidTracker tracker = GetTracker();
|
||||
if (tracker == null)
|
||||
{
|
||||
Log.Error("CustomRaidTracker not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 获取当前波次
|
||||
int currentWave = tracker.GetCurrentWave(raidDef);
|
||||
Log.Message($"Current wave: {currentWave}");
|
||||
|
||||
// 计算袭击规模
|
||||
int raidSize = CalculateRaidSize(currentWave, raidDef);
|
||||
Log.Message($"Calculated raid size: {raidSize}");
|
||||
|
||||
// 选择波次定义
|
||||
RaidWaveDef waveDef = SelectWaveForSize(raidSize, raidDef);
|
||||
if (waveDef == null)
|
||||
{
|
||||
Log.Error($"No wave found for raid size {raidSize}");
|
||||
return false;
|
||||
}
|
||||
Log.Message($"Selected wave: {waveDef.defName}");
|
||||
|
||||
// 设置派系
|
||||
parms.faction = Find.FactionManager.FirstFactionOfDef(raidDef.factionDef);
|
||||
if (parms.faction == null)
|
||||
{
|
||||
Log.Error($"Faction {raidDef.factionDef.defName} not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置点数
|
||||
parms.points = CalculateThreatPoints(raidSize);
|
||||
Log.Message($"Threat points: {parms.points}");
|
||||
|
||||
// 设置袭击策略
|
||||
parms.raidStrategy = RaidStrategyDefOf.ImmediateAttack;
|
||||
|
||||
// 设置自定义参数
|
||||
parms.SetCustomRaidWave(waveDef);
|
||||
parms.SetCustomRaidSize(raidSize);
|
||||
parms.SetCustomRaidDef(raidDef);
|
||||
parms.SetCustomRaidWaveNumber(currentWave);
|
||||
|
||||
Log.Message($"Custom raid parameters set: wave={waveDef.defName}, size={raidSize}, waveNum={currentWave}");
|
||||
|
||||
// 执行袭击
|
||||
bool success = base.TryExecuteWorker(parms);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// 成功执行后增加波次
|
||||
tracker.IncrementWave(raidDef);
|
||||
Log.Message($"Custom raid wave {currentWave + 1} executed successfully. Next wave will be {currentWave + 2}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Custom raid execution failed");
|
||||
}
|
||||
|
||||
Log.Message("=== Custom Raid Incident Finished ===");
|
||||
return success;
|
||||
}
|
||||
|
||||
protected override bool TryResolveRaidFaction(IncidentParms parms)
|
||||
{
|
||||
// 对于自定义袭击,我们已经通过扩展设置了派系
|
||||
if (parms.faction != null)
|
||||
{
|
||||
Log.Message($"Raid faction resolved: {parms.faction.Name}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果没有设置派系,尝试从自定义袭击定义中获取
|
||||
CustomRaidDef raidDef = parms.GetCustomRaidDef();
|
||||
if (raidDef?.factionDef != null)
|
||||
{
|
||||
parms.faction = Find.FactionManager.FirstFactionOfDef(raidDef.factionDef);
|
||||
bool success = parms.faction != null;
|
||||
Log.Message($"Resolved faction from raidDef: {raidDef.factionDef.defName}, success: {success}");
|
||||
return success;
|
||||
}
|
||||
|
||||
Log.Warning("Could not resolve raid faction");
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ResolveRaidStrategy(IncidentParms parms, PawnGroupKindDef groupKind)
|
||||
{
|
||||
// 如果已经设置了袭击策略,直接使用
|
||||
if (parms.raidStrategy != null)
|
||||
{
|
||||
Log.Message($"Raid strategy already set: {parms.raidStrategy.defName}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 从自定义波次定义中获取策略
|
||||
RaidWaveDef waveDef = parms.GetCustomRaidWave();
|
||||
if (waveDef != null)
|
||||
{
|
||||
// 这里可以根据 waveDef 的内容设置不同的策略
|
||||
// 例如,如果有特定标签就使用特定策略
|
||||
parms.raidStrategy = RaidStrategyDefOf.ImmediateAttack;
|
||||
Log.Message($"Set raid strategy from waveDef: {parms.raidStrategy.defName}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认策略
|
||||
parms.raidStrategy = RaidStrategyDefOf.ImmediateAttack;
|
||||
Log.Message($"Set default raid strategy: {parms.raidStrategy.defName}");
|
||||
}
|
||||
|
||||
protected override void ResolveRaidPoints(IncidentParms parms)
|
||||
{
|
||||
// 如果已经设置了点数,直接使用
|
||||
if (parms.points > 0)
|
||||
{
|
||||
Log.Message($"Raid points already set: {parms.points}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 从自定义袭击规模计算点数
|
||||
int raidSize = parms.GetCustomRaidSize();
|
||||
if (raidSize > 0)
|
||||
{
|
||||
parms.points = CalculateThreatPoints(raidSize);
|
||||
Log.Message($"Set raid points from custom size: {raidSize} -> {parms.points}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 回退到原版点数计算
|
||||
parms.points = StorytellerUtility.DefaultThreatPointsNow(parms.target);
|
||||
Log.Message($"Set raid points from default calculation: {parms.points}");
|
||||
}
|
||||
|
||||
protected override string GetLetterLabel(IncidentParms parms)
|
||||
{
|
||||
// 自定义袭击的信件标签
|
||||
RaidWaveDef waveDef = parms.GetCustomRaidWave();
|
||||
int waveNumber = parms.GetCustomRaidWaveNumber();
|
||||
CustomRaidDef raidDef = parms.GetCustomRaidDef();
|
||||
|
||||
if (waveDef != null && raidDef != null)
|
||||
{
|
||||
return $"Special Attack Wave {waveNumber + 1} - {waveDef.label ?? waveDef.defName}";
|
||||
}
|
||||
|
||||
return "Special Attack";
|
||||
}
|
||||
|
||||
protected override string GetLetterText(IncidentParms parms, List<Pawn> pawns)
|
||||
{
|
||||
// 自定义袭击的信件文本
|
||||
RaidWaveDef waveDef = parms.GetCustomRaidWave();
|
||||
int waveNumber = parms.GetCustomRaidWaveNumber();
|
||||
Faction faction = parms.faction;
|
||||
|
||||
string waveName = waveDef?.label ?? waveDef?.defName ?? "Unknown";
|
||||
string baseText = $"A special attack wave {waveNumber + 1} - {waveName} from {faction.Name} is approaching!";
|
||||
|
||||
// 添加袭击策略信息
|
||||
if (parms.raidStrategy != null)
|
||||
{
|
||||
baseText += "\n\n" + parms.raidStrategy.arrivalTextEnemy;
|
||||
}
|
||||
|
||||
return baseText;
|
||||
}
|
||||
|
||||
protected override LetterDef GetLetterDef()
|
||||
{
|
||||
// 使用威胁大的信件定义
|
||||
return LetterDefOf.ThreatBig;
|
||||
}
|
||||
|
||||
protected override string GetRelatedPawnsInfoLetterText(IncidentParms parms)
|
||||
{
|
||||
// 如果有相关pawn的信息,返回相应的文本
|
||||
return "LetterRelatedPawnsRaid".Translate(Faction.OfPlayer.def.pawnsPlural, parms.faction.def.pawnsPlural);
|
||||
}
|
||||
|
||||
// 自定义方法
|
||||
private CustomRaidDef GetCustomRaidDef()
|
||||
{
|
||||
// 从 DefDatabase 获取自定义袭击定义
|
||||
return DefDatabase<CustomRaidDef>.GetNamedSilentFail("ARA_SpecialAttack");
|
||||
}
|
||||
|
||||
private int CalculateRaidSize(int currentWave, CustomRaidDef raidDef)
|
||||
{
|
||||
int baseSize = raidDef.baseRaidNembers;
|
||||
var growthConfig = raidDef.pointsGrowthPerWave;
|
||||
|
||||
Log.Message($"Calculating raid size: base={baseSize}, wave={currentWave}, growthType={growthConfig.growthType}");
|
||||
|
||||
if (growthConfig.growthType == "Linear")
|
||||
{
|
||||
int result = baseSize + (int)(currentWave * growthConfig.linearGrowth);
|
||||
Log.Message($"Linear growth: {baseSize} + ({currentWave} * {growthConfig.linearGrowth}) = {result}");
|
||||
return result;
|
||||
}
|
||||
else if (growthConfig.growthType == "Exponential")
|
||||
{
|
||||
int result = (int)(baseSize * System.Math.Pow(growthConfig.exponentialBase, currentWave));
|
||||
Log.Message($"Exponential growth: {baseSize} * {growthConfig.exponentialBase}^{currentWave} = {result}");
|
||||
return result;
|
||||
}
|
||||
|
||||
// 默认线性增长
|
||||
int defaultResult = baseSize + currentWave;
|
||||
Log.Message($"Default growth: {baseSize} + {currentWave} = {defaultResult}");
|
||||
return defaultResult;
|
||||
}
|
||||
|
||||
private RaidWaveDef SelectWaveForSize(int raidSize, CustomRaidDef raidDef)
|
||||
{
|
||||
Log.Message($"Selecting wave for size: {raidSize}");
|
||||
|
||||
foreach (var poolRange in raidDef.pointWavePools)
|
||||
{
|
||||
bool minCondition = raidSize >= poolRange.minPoints;
|
||||
bool maxCondition = poolRange.maxPoints >= 99999f || raidSize < poolRange.maxPoints;
|
||||
|
||||
Log.Message($"Checking pool range: min={poolRange.minPoints}, max={poolRange.maxPoints}, matches={minCondition && maxCondition}");
|
||||
|
||||
if (minCondition && maxCondition)
|
||||
{
|
||||
var selectedWave = SelectWaveFromPool(poolRange.wavePool);
|
||||
Log.Message($"Selected wave from pool {poolRange.wavePool.defName}: {selectedWave?.defName}");
|
||||
return selectedWave;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有匹配的区间,返回最后一个池
|
||||
if (raidDef.pointWavePools.Count > 0)
|
||||
{
|
||||
var lastPool = raidDef.pointWavePools[raidDef.pointWavePools.Count - 1];
|
||||
var selectedWave = SelectWaveFromPool(lastPool.wavePool);
|
||||
Log.Message($"Selected wave from last pool {lastPool.wavePool.defName}: {selectedWave?.defName}");
|
||||
return selectedWave;
|
||||
}
|
||||
|
||||
Log.Error("No wave pools found in CustomRaidDef");
|
||||
return null;
|
||||
}
|
||||
|
||||
private RaidWaveDef SelectWaveFromPool(RaidWavePoolDef wavePool)
|
||||
{
|
||||
if (wavePool == null)
|
||||
{
|
||||
Log.Error("WavePool is null");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (wavePool.waves.NullOrEmpty())
|
||||
{
|
||||
Log.Error($"WavePool {wavePool.defName} has no waves");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果有权重配置,使用权重随机
|
||||
if (wavePool.selectionWeights != null && wavePool.selectionWeights.Count > 0)
|
||||
{
|
||||
var weightedWaves = wavePool.waves.Where(w => wavePool.selectionWeights.ContainsKey(w.defName)).ToList();
|
||||
if (weightedWaves.Any())
|
||||
{
|
||||
var selected = weightedWaves.RandomElementByWeight(waveDef => wavePool.selectionWeights[waveDef.defName]);
|
||||
Log.Message($"Selected weighted wave: {selected.defName}");
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
// 否则均匀随机
|
||||
var randomWave = wavePool.waves.RandomElement();
|
||||
Log.Message($"Selected random wave: {randomWave.defName}");
|
||||
return randomWave;
|
||||
}
|
||||
|
||||
private float CalculateThreatPoints(int raidSize)
|
||||
{
|
||||
// 根据袭击规模计算威胁点数
|
||||
// 这里可以基于原版的威胁点数计算逻辑进行调整
|
||||
float points = raidSize * 100f;
|
||||
Log.Message($"Calculated threat points: {raidSize} * 100 = {points}");
|
||||
return points;
|
||||
}
|
||||
|
||||
// 重写生成pawn的方法,确保使用自定义波次定义
|
||||
public override void ResolveRaidArriveMode(IncidentParms parms)
|
||||
{
|
||||
if (parms.raidArrivalMode != null)
|
||||
{
|
||||
Log.Message($"Raid arrival mode already set: {parms.raidArrivalMode.defName}");
|
||||
return;
|
||||
}
|
||||
// 对于自定义袭击,默认使用边缘进入
|
||||
parms.raidArrivalMode = PawnsArrivalModeDefOf.EdgeWalkIn;
|
||||
Log.Message($"Set raid arrival mode: {parms.raidArrivalMode.defName}");
|
||||
}
|
||||
|
||||
// 可选:重写其他方法以提供更好的调试信息
|
||||
public override string ToString()
|
||||
{
|
||||
return base.ToString() + " (CustomRaid)";
|
||||
}
|
||||
|
||||
public static void TestCustomRaid()
|
||||
{
|
||||
Map map = Find.CurrentMap;
|
||||
if (map == null)
|
||||
{
|
||||
Log.Error("No current map found");
|
||||
return;
|
||||
}
|
||||
|
||||
IncidentDef raidIncident = DefDatabase<IncidentDef>.GetNamed("CustomRaidIncident");
|
||||
if (raidIncident != null)
|
||||
{
|
||||
var parms = StorytellerUtility.DefaultParmsNow(raidIncident.category, map);
|
||||
bool success = raidIncident.Worker.TryExecute(parms);
|
||||
Messages.Message(success ? "Custom raid test executed!" : "Custom raid test failed",
|
||||
success ? MessageTypeDefOf.PositiveEvent : MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("CustomRaidIncident not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Source/ArachnaeSwarm/Storyteller/RaidWaveDef.cs
Normal file
37
Source/ArachnaeSwarm/Storyteller/RaidWaveDef.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class RaidWaveDef : Def
|
||||
{
|
||||
public List<PawnComposition> pawnComposition;
|
||||
|
||||
public override IEnumerable<string> ConfigErrors()
|
||||
{
|
||||
foreach (string error in base.ConfigErrors())
|
||||
{
|
||||
yield return error;
|
||||
}
|
||||
|
||||
if (pawnComposition.NullOrEmpty())
|
||||
{
|
||||
yield return "pawnComposition is empty";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class PawnComposition
|
||||
{
|
||||
public PawnKindDef pawnKind;
|
||||
public float ratio = 1f;
|
||||
public int minCount = 0;
|
||||
public int maxCount = 0; // 0表示无限制
|
||||
public bool DefaultUnit = false;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{pawnKind?.defName ?? "null"} (ratio: {ratio}, min: {minCount}, max: {maxCount}, default: {DefaultUnit})";
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Source/ArachnaeSwarm/Storyteller/RaidWavePoolDef.cs
Normal file
24
Source/ArachnaeSwarm/Storyteller/RaidWavePoolDef.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class RaidWavePoolDef : Def
|
||||
{
|
||||
public List<RaidWaveDef> waves;
|
||||
public Dictionary<string, float> selectionWeights;
|
||||
|
||||
public override IEnumerable<string> ConfigErrors()
|
||||
{
|
||||
foreach (string error in base.ConfigErrors())
|
||||
{
|
||||
yield return error;
|
||||
}
|
||||
|
||||
if (waves.NullOrEmpty())
|
||||
{
|
||||
yield return "waves list is empty";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user