zc
This commit is contained in:
Binary file not shown.
@@ -1,15 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class MapComponent_SkyfallerDelayed : MapComponent
|
||||
{
|
||||
private List<DelayedSkyfaller> scheduledSkyfallers = new List<DelayedSkyfaller>();
|
||||
|
||||
|
||||
public MapComponent_SkyfallerDelayed(Map map) : base(map) { }
|
||||
|
||||
|
||||
public void ScheduleSkyfaller(ThingDef skyfallerDef, IntVec3 targetCell, int delayTicks, Pawn caster = null)
|
||||
{
|
||||
scheduledSkyfallers.Add(new DelayedSkyfaller
|
||||
@@ -20,14 +20,12 @@ namespace WulaFallenEmpire
|
||||
caster = caster
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override void MapComponentTick()
|
||||
{
|
||||
base.MapComponentTick();
|
||||
|
||||
|
||||
int currentTick = Find.TickManager.TicksGame;
|
||||
|
||||
// 检查并执行到期的召唤
|
||||
for (int i = scheduledSkyfallers.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var skyfaller = scheduledSkyfallers[i];
|
||||
@@ -38,16 +36,20 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void SpawnSkyfaller(DelayedSkyfaller delayedSkyfaller)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (delayedSkyfaller.skyfallerDef != null && delayedSkyfaller.targetCell.IsValid && delayedSkyfaller.targetCell.InBounds(map))
|
||||
if (delayedSkyfaller.skyfallerDef == null) return;
|
||||
if (!delayedSkyfaller.targetCell.IsValid || !delayedSkyfaller.targetCell.InBounds(map)) return;
|
||||
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(delayedSkyfaller.skyfallerDef);
|
||||
GenSpawn.Spawn(skyfaller, delayedSkyfaller.targetCell, map);
|
||||
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(delayedSkyfaller.skyfallerDef);
|
||||
GenSpawn.Spawn(skyfaller, delayedSkyfaller.targetCell, map);
|
||||
Log.Message($"[DelayedSkyfaller] Spawned skyfaller at {delayedSkyfaller.targetCell}");
|
||||
Log.Message($"[DelayedSkyfaller] Spawned '{delayedSkyfaller.skyfallerDef.defName}' at {delayedSkyfaller.targetCell}");
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
@@ -55,21 +57,21 @@ namespace WulaFallenEmpire
|
||||
Log.Error($"[DelayedSkyfaller] Error spawning skyfaller: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Collections.Look(ref scheduledSkyfallers, "scheduledSkyfallers", LookMode.Deep);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DelayedSkyfaller : IExposable
|
||||
{
|
||||
public ThingDef skyfallerDef;
|
||||
public IntVec3 targetCell;
|
||||
public int spawnTick;
|
||||
public Pawn caster;
|
||||
|
||||
|
||||
public void ExposeData()
|
||||
{
|
||||
Scribe_Defs.Look(ref skyfallerDef, "skyfallerDef");
|
||||
@@ -79,3 +81,4 @@ namespace WulaFallenEmpire
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire.EventSystem.AI.Tools
|
||||
{
|
||||
public class Tool_CallBombardment : AITool
|
||||
{
|
||||
public override string Name => "call_bombardment";
|
||||
public override string Description => "Calls orbital bombardment support at a specified map coordinate using an AbilityDef's bombardment configuration (e.g., WULA_Firepower_Cannon_Salvo).";
|
||||
public override string UsageSchema => "<call_bombardment><abilityDef>string (optional, default WULA_Firepower_Cannon_Salvo)</abilityDef><x>int</x><z>int</z><cell>x,z (optional)</cell><filterFriendlyFire>true/false (optional, default true)</filterFriendlyFire></call_bombardment>";
|
||||
|
||||
public override string Execute(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
var parsed = ParseXmlArgs(args);
|
||||
|
||||
string abilityDefName = parsed.TryGetValue("abilityDef", out var abilityStr) && !string.IsNullOrWhiteSpace(abilityStr)
|
||||
? abilityStr.Trim()
|
||||
: "WULA_Firepower_Cannon_Salvo";
|
||||
|
||||
if (!TryParseTargetCell(parsed, out var targetCell))
|
||||
{
|
||||
return "Error: Missing target coordinates. Provide <x> and <z> (or <cell>x,z</cell>).";
|
||||
}
|
||||
|
||||
Map map = Find.CurrentMap;
|
||||
if (map == null) return "Error: No active map.";
|
||||
if (!targetCell.InBounds(map)) return $"Error: Target {targetCell} is out of bounds.";
|
||||
|
||||
AbilityDef abilityDef = DefDatabase<AbilityDef>.GetNamed(abilityDefName, false);
|
||||
if (abilityDef == null) return $"Error: AbilityDef '{abilityDefName}' not found.";
|
||||
|
||||
var bombardmentProps = abilityDef.comps?.OfType<CompProperties_AbilityCircularBombardment>().FirstOrDefault();
|
||||
if (bombardmentProps == null) return $"Error: AbilityDef '{abilityDefName}' has no CompProperties_AbilityCircularBombardment.";
|
||||
if (bombardmentProps.skyfallerDef == null) return $"Error: AbilityDef '{abilityDefName}' has no skyfallerDef configured.";
|
||||
|
||||
bool filterFriendlyFire = true;
|
||||
if (parsed.TryGetValue("filterFriendlyFire", out var ffStr) && bool.TryParse(ffStr, out bool ff))
|
||||
{
|
||||
filterFriendlyFire = ff;
|
||||
}
|
||||
|
||||
List<IntVec3> selectedTargets = SelectTargetCells(map, targetCell, bombardmentProps, filterFriendlyFire);
|
||||
if (selectedTargets.Count == 0) return $"Error: No valid target cells near {targetCell}.";
|
||||
|
||||
bool isPaused = Find.TickManager != null && Find.TickManager.Paused;
|
||||
int totalLaunches = ScheduleBombardment(map, selectedTargets, bombardmentProps, spawnImmediately: isPaused);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("Success: Bombardment scheduled.");
|
||||
sb.AppendLine($"- abilityDef: {abilityDefName}");
|
||||
sb.AppendLine($"- center: {targetCell}");
|
||||
sb.AppendLine($"- skyfallerDef: {bombardmentProps.skyfallerDef.defName}");
|
||||
sb.AppendLine($"- launches: {totalLaunches}/{bombardmentProps.maxLaunches}");
|
||||
sb.AppendLine($"- mode: {(isPaused ? "spawned immediately (game paused)" : "delayed schedule")}");
|
||||
sb.AppendLine("- prereqs: ignored (facility/cooldown/non-hostility/research)");
|
||||
return sb.ToString().TrimEnd();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryParseTargetCell(Dictionary<string, string> parsed, out IntVec3 cell)
|
||||
{
|
||||
cell = IntVec3.Invalid;
|
||||
|
||||
if (parsed.TryGetValue("x", out var xStr) && parsed.TryGetValue("z", out var zStr) &&
|
||||
int.TryParse(xStr, out int x) && int.TryParse(zStr, out int z))
|
||||
{
|
||||
cell = new IntVec3(x, 0, z);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (parsed.TryGetValue("cell", out var cellStr) && !string.IsNullOrWhiteSpace(cellStr))
|
||||
{
|
||||
var parts = cellStr.Split(new[] { ',', '\uFF0C', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (parts.Length >= 2 && int.TryParse(parts[0], out int cx) && int.TryParse(parts[1], out int cz))
|
||||
{
|
||||
cell = new IntVec3(cx, 0, cz);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<IntVec3> SelectTargetCells(Map map, IntVec3 center, CompProperties_AbilityCircularBombardment props, bool filterFriendlyFire)
|
||||
{
|
||||
var candidates = GenRadial.RadialCellsAround(center, props.radius, true)
|
||||
.Where(c => c.InBounds(map))
|
||||
.Where(c => IsValidTargetCell(map, c, center, props, filterFriendlyFire))
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0) return new List<IntVec3>();
|
||||
|
||||
var selected = new List<IntVec3>();
|
||||
foreach (var cell in candidates.InRandomOrder())
|
||||
{
|
||||
if (Rand.Value <= props.targetSelectionChance)
|
||||
{
|
||||
selected.Add(cell);
|
||||
}
|
||||
|
||||
if (selected.Count >= props.maxTargets) break;
|
||||
}
|
||||
|
||||
if (selected.Count < props.minTargets)
|
||||
{
|
||||
var missedCells = candidates.Except(selected).InRandomOrder().ToList();
|
||||
int needed = props.minTargets - selected.Count;
|
||||
if (needed > 0 && missedCells.Count > 0)
|
||||
{
|
||||
selected.AddRange(missedCells.Take(Math.Min(needed, missedCells.Count)));
|
||||
}
|
||||
}
|
||||
else if (selected.Count > props.maxTargets)
|
||||
{
|
||||
selected = selected.InRandomOrder().Take(props.maxTargets).ToList();
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
private static bool IsValidTargetCell(Map map, IntVec3 cell, IntVec3 center, CompProperties_AbilityCircularBombardment props, bool filterFriendlyFire)
|
||||
{
|
||||
if (props.minDistanceFromCenter > 0f)
|
||||
{
|
||||
float distance = Vector3.Distance(cell.ToVector3(), center.ToVector3());
|
||||
if (distance < props.minDistanceFromCenter) return false;
|
||||
}
|
||||
|
||||
if (props.avoidBuildings && cell.GetEdifice(map) != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterFriendlyFire && props.avoidFriendlyFire)
|
||||
{
|
||||
var things = map.thingGrid.ThingsListAt(cell);
|
||||
if (things != null)
|
||||
{
|
||||
for (int i = 0; i < things.Count; i++)
|
||||
{
|
||||
if (things[i] is Pawn pawn && pawn.Faction == Faction.OfPlayer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int ScheduleBombardment(Map map, List<IntVec3> targets, CompProperties_AbilityCircularBombardment props, bool spawnImmediately)
|
||||
{
|
||||
int now = Find.TickManager?.TicksGame ?? 0;
|
||||
int startTick = now + props.warmupTicks;
|
||||
int launchesCompleted = 0;
|
||||
int groupIndex = 0;
|
||||
|
||||
var remainingTargets = new List<IntVec3>(targets);
|
||||
|
||||
MapComponent_SkyfallerDelayed delayed = null;
|
||||
if (!spawnImmediately)
|
||||
{
|
||||
delayed = map.GetComponent<MapComponent_SkyfallerDelayed>();
|
||||
if (delayed == null)
|
||||
{
|
||||
delayed = new MapComponent_SkyfallerDelayed(map);
|
||||
map.components.Add(delayed);
|
||||
}
|
||||
}
|
||||
|
||||
while (remainingTargets.Count > 0 && launchesCompleted < props.maxLaunches)
|
||||
{
|
||||
int groupSize = Math.Min(props.simultaneousLaunches, remainingTargets.Count);
|
||||
var groupTargets = remainingTargets.Take(groupSize).ToList();
|
||||
remainingTargets.RemoveRange(0, groupSize);
|
||||
|
||||
if (props.useIndependentIntervals)
|
||||
{
|
||||
for (int i = 0; i < groupTargets.Count && launchesCompleted < props.maxLaunches; i++)
|
||||
{
|
||||
int scheduledTick = startTick + groupIndex * props.launchIntervalTicks + i * props.innerLaunchIntervalTicks;
|
||||
SpawnOrSchedule(map, delayed, props.skyfallerDef, groupTargets[i], spawnImmediately, scheduledTick - now);
|
||||
launchesCompleted++;
|
||||
}
|
||||
groupIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int scheduledTick = startTick + groupIndex * props.launchIntervalTicks;
|
||||
for (int i = 0; i < groupTargets.Count && launchesCompleted < props.maxLaunches; i++)
|
||||
{
|
||||
SpawnOrSchedule(map, delayed, props.skyfallerDef, groupTargets[i], spawnImmediately, scheduledTick - now);
|
||||
launchesCompleted++;
|
||||
}
|
||||
groupIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
return launchesCompleted;
|
||||
}
|
||||
|
||||
private static void SpawnOrSchedule(Map map, MapComponent_SkyfallerDelayed delayed, ThingDef skyfallerDef, IntVec3 cell, bool spawnImmediately, int delayTicks)
|
||||
{
|
||||
if (!cell.IsValid || !cell.InBounds(map)) return;
|
||||
|
||||
if (spawnImmediately || delayTicks <= 0)
|
||||
{
|
||||
Skyfaller skyfaller = SkyfallerMaker.MakeSkyfaller(skyfallerDef);
|
||||
GenSpawn.Spawn(skyfaller, cell, map);
|
||||
return;
|
||||
}
|
||||
|
||||
delayed?.ScheduleSkyfaller(skyfallerDef, cell, delayTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,18 @@ using Verse;
|
||||
|
||||
namespace WulaFallenEmpire.EventSystem.AI.Tools
|
||||
{
|
||||
public class Tool_GetColonistStatus : AITool
|
||||
{
|
||||
public override string Name => "get_colonist_status";
|
||||
public override string Description => "Returns detailed status of colonists. Can be filtered to find the colonist in the worst condition (e.g., lowest mood, most injured). This helps the AI understand the colony's state without needing to know specific names.";
|
||||
public override string UsageSchema => "<get_colonist_status><filter>string (optional, can be 'lowest_mood', 'most_injured', 'hungriest', 'most_tired')</filter><showAllNeeds>true/false (optional, default true in DevMode, false otherwise)</showAllNeeds><lowNeedThreshold>float 0-1 (optional, default 0.3)</lowNeedThreshold></get_colonist_status>";
|
||||
public class Tool_GetColonistStatus : AITool
|
||||
{
|
||||
public override string Name => "get_colonist_status";
|
||||
public override string Description => "Returns detailed status of colonists. Can be filtered to find the colonist in the worst condition (e.g., lowest mood, most injured). This helps the AI understand the colony's state without needing to know specific names.";
|
||||
public override string UsageSchema => "<get_colonist_status><filter>string (optional, can be 'lowest_mood', 'most_injured', 'hungriest', 'most_tired')</filter><showAllNeeds>true/false (optional, default true)</showAllNeeds><lowNeedThreshold>float 0-1 (optional, default 0.3)</lowNeedThreshold></get_colonist_status>";
|
||||
|
||||
public override string Execute(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
string filter = null;
|
||||
bool showAllNeeds = Prefs.DevMode;
|
||||
bool showAllNeeds = true;
|
||||
float lowNeedThreshold = 0.3f;
|
||||
|
||||
if (!string.IsNullOrEmpty(args))
|
||||
|
||||
@@ -153,20 +153,7 @@ namespace WulaFallenEmpire.EventSystem.AI.Tools
|
||||
|
||||
if (thingsToDrop.Count > 0)
|
||||
{
|
||||
// If the conversation window pauses the game, incoming drop pods may not "land" until unpaused.
|
||||
// To keep this tool reliable, place items immediately when paused; otherwise, use drop pods.
|
||||
bool isPaused = Find.TickManager != null && Find.TickManager.Paused;
|
||||
if (isPaused)
|
||||
{
|
||||
foreach (var thing in thingsToDrop)
|
||||
{
|
||||
GenPlace.TryPlaceThing(thing, dropSpot, map, ThingPlaceMode.Near);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DropPodUtility.DropThingsNear(dropSpot, map, thingsToDrop);
|
||||
}
|
||||
DropPodUtility.DropThingsNear(dropSpot, map, thingsToDrop);
|
||||
|
||||
Faction faction = Find.FactionManager.FirstFactionOfDef(FactionDef.Named("Wula_PIA_Legion_Faction"));
|
||||
// Avoid unresolved named placeholders if the translation system doesn't pick up NamedArguments as expected.
|
||||
@@ -176,7 +163,7 @@ namespace WulaFallenEmpire.EventSystem.AI.Tools
|
||||
Messages.Message(letterText, new LookTargets(dropSpot, map), MessageTypeDefOf.PositiveEvent);
|
||||
|
||||
resultLog.Length -= 2; // Remove trailing comma
|
||||
resultLog.Append($" at {dropSpot}. {(isPaused ? "(placed immediately because game is paused)" : "(drop pods inbound)")}");
|
||||
resultLog.Append($" at {dropSpot}. (drop pods inbound)");
|
||||
|
||||
if (Prefs.DevMode && substitutions.Count > 0)
|
||||
{
|
||||
|
||||
@@ -179,6 +179,24 @@ Usage:
|
||||
<maxResults>50</maxResults>
|
||||
</get_map_pawns>
|
||||
|
||||
## call_bombardment
|
||||
Description: Calls orbital bombardment support at a specified map coordinate using an AbilityDef's bombardment configuration (e.g., WULA_Firepower_Cannon_Salvo).
|
||||
Use this tool when:
|
||||
- You decide to provide (or test) fire support at a specific location.
|
||||
Parameters:
|
||||
- abilityDef: (OPTIONAL) AbilityDef defName (default WULA_Firepower_Cannon_Salvo).
|
||||
- x/z: (REQUIRED) Target cell coordinates on the current map.
|
||||
- cell: (OPTIONAL) Alternative to x/z: ""x,z"".
|
||||
- filterFriendlyFire: (OPTIONAL) true/false, avoid targeting player's pawns when possible (default true).
|
||||
Notes:
|
||||
- This tool ignores ability prerequisites (facility/cooldown/non-hostility/research).
|
||||
Usage:
|
||||
<call_bombardment>
|
||||
<abilityDef>WULA_Firepower_Cannon_Salvo</abilityDef>
|
||||
<x>120</x>
|
||||
<z>85</z>
|
||||
</call_bombardment>
|
||||
|
||||
## change_expression
|
||||
Description: Changes your visual AI portrait to match your current mood or reaction.
|
||||
Use this tool when:
|
||||
@@ -250,6 +268,7 @@ When the player requests any form of resources, you MUST follow this multi-turn
|
||||
_tools.Add(new Tool_GetColonistStatus());
|
||||
_tools.Add(new Tool_GetMapResources());
|
||||
_tools.Add(new Tool_GetMapPawns());
|
||||
_tools.Add(new Tool_CallBombardment());
|
||||
_tools.Add(new Tool_ChangeExpression());
|
||||
_tools.Add(new Tool_SearchThingDef());
|
||||
}
|
||||
|
||||
@@ -45,16 +45,12 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a natural language request string into a list of spawnable items.
|
||||
/// Example: "5 beef, 1 persona core, 30 wood"
|
||||
/// </summary>
|
||||
public static List<SearchResult> ParseAndSearch(string request)
|
||||
{
|
||||
var results = new List<SearchResult>();
|
||||
if (string.IsNullOrEmpty(request)) return results;
|
||||
|
||||
var parts = request.Split(new[] { ',', ',', ';', '、', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var parts = request.Split(new[] { ',', '\uFF0C', ';', '\u3001', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
var result = ParseSingleItem(part.Trim());
|
||||
@@ -88,7 +84,7 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
{
|
||||
count = parsedCount;
|
||||
nameQuery = itemRequest.Replace(match.Value, "").Trim();
|
||||
nameQuery = Regex.Replace(nameQuery, @"[个只把张条xX×]", "").Trim();
|
||||
nameQuery = Regex.Replace(nameQuery, @"[\u4E2A\u53EA\u628A\u5F20\u6761xX\u00D7]", "").Trim();
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(nameQuery))
|
||||
@@ -150,8 +146,12 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
}
|
||||
}
|
||||
|
||||
bool queryLooksLikeFood = tokens.Any(t => t == "meal" || t == "food" || t.Contains("meal") || t.Contains("food")) ||
|
||||
lowerQuery.Contains("食") || lowerQuery.Contains("饭") || lowerQuery.Contains("餐");
|
||||
bool queryLooksLikeFood =
|
||||
tokens.Any(t => t == "meal" || t == "food" || t.Contains("meal") || t.Contains("food")) ||
|
||||
lowerQuery.Contains("\u996D") || // 饭
|
||||
lowerQuery.Contains("\u9910") || // 餐
|
||||
lowerQuery.Contains("\u98DF"); // 食
|
||||
|
||||
if (queryLooksLikeFood && def.ingestible != null)
|
||||
{
|
||||
score += 0.05f;
|
||||
@@ -179,12 +179,11 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
if (string.IsNullOrWhiteSpace(cleaned)) continue;
|
||||
tokens.Add(cleaned);
|
||||
|
||||
// CJK queries often have no spaces; add bigrams for better partial matching
|
||||
// (e.g. "乌拉能源核心" should match "乌拉帝国能源核心").
|
||||
// CJK queries often have no spaces; add bigrams for better partial matching.
|
||||
// e.g. "乌拉能源核心" should match "乌拉帝国能源核心".
|
||||
AddCjkBigrams(cleaned, tokens);
|
||||
}
|
||||
|
||||
// For queries like "fine meal" also consider the normalized concatenation for matching "MealFine".
|
||||
if (tokens.Count >= 2)
|
||||
{
|
||||
tokens.Add(string.Concat(tokens));
|
||||
@@ -226,7 +225,6 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
int len = end - start + 1;
|
||||
if (len < 2) return;
|
||||
|
||||
// cap to avoid generating too many tokens for long Chinese sentences
|
||||
int maxBigrams = 32;
|
||||
int added = 0;
|
||||
|
||||
@@ -240,7 +238,6 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
|
||||
private static bool IsCjkChar(char c)
|
||||
{
|
||||
// Basic CJK ranges commonly used in Chinese/Japanese/Korean text.
|
||||
return (c >= '\u4E00' && c <= '\u9FFF') ||
|
||||
(c >= '\u3400' && c <= '\u4DBF') ||
|
||||
(c >= '\uF900' && c <= '\uFAFF');
|
||||
@@ -256,3 +253,4 @@ namespace WulaFallenEmpire.EventSystem.AI.Utils
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user