Skip to content

EA 23.315 Nightly โ€‹

June 10, 2026

43 files modified. 26 new files created.

Important Changes โ€‹

Possible breaking changes. Click the filename to view the chunk.

Card (1) โ€‹

cs
public string StripTalkSpeiclaCharacters(string text) 
public string StripTalkSpecialCharacters(string text) 

Chara (1) โ€‹

cs
public bool CanDuplicate() 
public bool CanDuplicate(DuplicateCondition con = DuplicateCondition.Default) 

CustomMerchantStock (1) โ€‹

cs
public static CustomMerchantStock CreateFromId(string stockId, ModPackage owner) 
public static CustomMerchantStock CreateFromId(string stockId, ModPackage owner = null) 

DramaManager (1) โ€‹

cs
public static Dictionary<string, ExcelData> dictCache = new Dictionary<string, ExcelData>(); 
public static Dictionary<string, ExcelData> dictCache = new Dictionary<string, ExcelData>(PathComparer.Default); 

FEAT (1) โ€‹

cs
public List<string> Apply(int a, ElementContainer owner, bool hint = false) 
public virtual List<string> Apply(int a, ElementContainer owner, bool hint = false) 

ModUtil (4) โ€‹

cs
public static void LogModError(string message, SourceData.BaseRow row = null) 
public static void LogModError(string message, SourceData.BaseRow row) 
cs
public static void LogModError(string message, FileInfo file = null) 
public static void LogModError(string message, FileInfo file) 
cs
public static void LogModError(string message, DirectoryInfo dir = null) 
public static void LogModError(string message, DirectoryInfo dir) 
cs
public static Sprite AppendSpriteSheet(string id, int resizeWidth = 0, int resizeHeight = 0, string pattern = "@") 
public static void RegisterElinEventAttributes() 

ReligionMachine (1) โ€‹

cs
public override void OnReforge(Thing t) 
public override bool IsIgnoreReforge(Thing t) 

ReligionWind (1) โ€‹

cs
public override void OnReforge(Thing t) 
public override bool IsIgnoreReforge(Thing t) 

AI_Idle โ€‹

public override IEnumerable<Status> Run()

cs
		}
		case "spider_queen":
		{
			if (EClass.rnd(20) != 0 || !owner.CanDuplicate() || EClass._zone.IsUserZone) 
			if (EClass.rnd(20) != 0 || !owner.CanDuplicate(DuplicateCondition.SpiderEgg) || EClass._zone.IsUserZone) 
			{
				break;
			}

public override IEnumerable<Status> Run()

cs
		}
		case "mech_scarab":
		{
			if (EClass.rnd(20) != 0 || !owner.CanDuplicate() || EClass._zone.IsUserZone) 
			if (EClass.rnd(20) != 0 || !owner.CanDuplicate(DuplicateCondition.Scarab) || EClass._zone.IsUserZone) 
			{
				break;
			}

ActEffect โ€‹

void AddCon<T>(int rate, int power) where T : Condition

cs
	switch (id)
	{
	case EffectId.Duplicate:
	{ 
		Point randomPoint = CC.pos.GetRandomPoint(2, requireLos: false, allowChara: false, allowBlocked: false, 200); 
		if (randomPoint == null || randomPoint.Equals(CC.pos) || !randomPoint.IsValid || !CC.CanDuplicate()) 
		{ 
			CC.Say("split_fail", CC); 
			return; 
		} 
		Chara t2 = CC.Duplicate(); 
		EClass._zone.AddCard(t2, randomPoint); 
		CC.Say("split", CC); 
		CC.TryDuplicate(); 
		break;
	} 
	case EffectId.Escape:
		if (CC.IsPCFaction || (EClass._zone.Boss == CC && EClass.rnd(30) != 0))
		{

void AddCon<T>(int rate, int power) where T : Condition

cs
	case EffectId.ThrowPotion:
		if (!CC.pos.Equals(TC.pos))
		{
			Thing t3 = ThingGen.Create(new string[6] { "330", "331", "334", "335", "336", "1142" }.RandomItem()); 
			ActThrow.Throw(CC, TC.pos, t3, ThrowMethod.Punish, 0.7f); 
			Thing t2 = ThingGen.Create(new string[6] { "330", "331", "334", "335", "336", "1142" }.RandomItem()); 
			ActThrow.Throw(CC, TC.pos, t2, ThrowMethod.Punish, 0.7f); 
		}
		break;
	case EffectId.StripBlessing:

public static bool Wish(string s, string name, int power, BlessedState state)

cs
	Msg.thirdPerson1.Set(EClass.pc);
	string netMsg = GameLang.Parse("wish".langGame(), thirdPerson: true, name, s);
	List<WishItem> list = new List<WishItem>();
	int wishLv = 10 + power / 4; 
	int wishValue = 5000 + power * 50; 
	long wishLv = 10 + power / 4; 
	int wishValue = MathEx.ClampToInt(5000 + (long)power * 50L); 
	if (state >= BlessedState.Blessed)
	{
		wishLv = wishLv * 150 / 100;

public static bool Wish(string s, string name, int power, BlessedState state)

cs
				{
					CardBlueprint.SetRarity(Rarity.Legendary);
				}
				Thing thing = ThingGen.Create(r.id, -1, wishLv); 
				Thing thing = ThingGen.Create(r.id, -1, MathEx.ClampToInt(wishLv)); 
				int num = 1;
				int price = thing.GetPrice(CurrencyType.Money, sell: false, PriceType.Tourism);
				bool flag2 = thing.trait is TraitDeed || thing.rarity >= Rarity.Artifact || thing.source._origin == "artifact_summon";

AttackProcess โ€‹

public void PlayRangedAnime(int numFire, float delay = 0f)

cs
	bool isGun = toolRange is TraitToolRangeGun;
	bool isCane = toolRange is TraitToolRangeCane;
	GameSetting.EffectData data = EClass.setting.effect.guns.TryGetValue(weapon.id) ?? EClass.setting.effect.guns[isCane ? "cane" : (isGun ? "gun" : "bow")];
	CustomGunEffectData customData = data as CustomGunEffectData; 
	bool isPCC = CC.IsPCC && CC.renderer.hasActor;
	Vector2 firePos = (isPCC ? new Vector2(data.firePos.x * (float)((CC.renderer.actor.currentDir != 0 && CC.renderer.actor.currentDir != 1) ? 1 : (-1)), data.firePos.y) : Vector2.zero);
	Chara _CC = CC;

public void PlayRangedAnime(int numFire, float delay = 0f)

cs
	Color effColor = Color.white;
	if (isCane)
	{
		IEnumerable<Element> enumerable = toolRange.owner.elements.dict.Values.Where((Element e) => e.source.categorySub == "eleAttack"); 
		if (enumerable.Count() > 0) 
		Element element = toolRange.owner.elements.dict.Values.Where((Element e) => e.source.categorySub == "eleAttack").RandomItem(); 
		if (element != null) 
		{
			Element element = enumerable.RandomItem(); 
			effColor = EClass.Colors.elementColors[element.source.alias]; 
			effColor = EClass.Colors.elementColors.TryGetValue(element.source.alias, "eleFire"); 
		} 
		if (!string.IsNullOrEmpty(customData?.caneColor)) 
		{ 
			Color color = customData.caneColor.ToColor(); 
			effColor = (customData.caneColorBlend ? Color.Lerp(effColor, color, 0.5f) : color); 
		}
	}
	bool isLaser = toolRange is TraitToolRangeGunEnergy || (customData?.forceLaser ?? false); 
	bool isRail = isLaser && (weapon.id == "gun_rail" || (customData?.forceRail ?? false)); 
	for (int i = 0; i < numFire; i++)
	{
		TweenUtil.Delay((float)i * data.delay + delay, delegate
		{
			if (EClass.core.IsGameStarted && _CC.IsAliveInCurrentZone && _zone == _CC.currentZone)
			{
				Vector3 fromV = (_CC.isSynced ? _CC.renderer.position : _CC.pos.Position()); 
				switch (_weapon.id)
				{
				case "gun_rail":

public void PlayRangedAnime(int numFire, float delay = 0f)

cs
					_CC.PlayEffect("laser").GetComponent<SpriteBasedLaser>().Play(_TP.PositionCenter());
					break;
				case "gun_laser_assault":
					Effect.Get("ranged_laser")._Play(_CC.pos, _CC.isSynced ? _CC.renderer.position : _CC.pos.Position(), 0f, _TP, data.sprite); 
					Effect.Get("ranged_laser")._Play(_CC.pos, fromV, 0f, _TP, data.sprite); 
					break;
				default:
				{ 
					Effect effect = Effect.Get("ranged_arrow")._Play(_CC.pos, _CC.isSynced ? _CC.renderer.position : _CC.pos.Position(), 0f, _TP, data.sprite); 
					if (isCane) 
					if (isLaser) 
					{
						effect.sr.color = effColor; 
						string id = (isRail ? "laser_rail" : "laser"); 
						_CC.PlayEffect(id).GetComponent<SpriteBasedLaser>().Play(_TP.PositionCenter()); 
					} 
					else
					{ 
						Effect effect = Effect.Get("ranged_arrow")._Play(_CC.pos, fromV, 0f, _TP, data.sprite); 
						if (isCane) 
						{ 
							effect.sr.color = effColor; 
						} 
					}
					break;
				}
				} 
				if (data.eject)
				{
					if (!ignoreSound)
					{
						_CC.PlaySound("bullet_drop"); 
						_CC.PlaySound(customData?.idSoundEject.IsEmpty("bullet_drop")); 
					}
					_CC.PlayEffect("bullet").Emit(1);
				}

CMOD โ€‹

public static class CMOD

cs

	public const string PriceCalcOverride = "price_calc_override";

	public const string DisableBarterChoice = "disable_barter_choice"; 
	public const string IsCustomContent = "custom_content";

	public const string IsCustomCwlContent = "cwl_tags_applied";

Card โ€‹

public void SetBool(string id, bool enable)

cs

	public int GetInt(string id, int? defaultInt = null)
	{
		return GetInt(id.GetHashCode(), defaultInt); 
		int @int = GetInt(id.GetHashCode(), defaultInt); 
		if (!IsPC) 
		{ 
			return @int; 
		} 
		return EClass.player.dialogFlags.GetValueOrDefault(id, @int); 
	}

	public void AddInt(string id, int value)
	{
		AddInt(id.GetHashCode(), value);
		if (IsPC) 
		{ 
			EClass.player.dialogFlags[id] = GetInt(id.GetHashCode()); 
		} 
	}

	public void SetInt(string id, int value = 0)
	{
		SetInt(id.GetHashCode(), value);
		if (IsPC) 
		{ 
			EClass.player.dialogFlags[id] = value; 
		} 
	}

	public string GetStr(string id, string defaultStr = null)

public void TalkRaw(string text, string ref1 = null, string ref2 = null, bool fo

cs
		text = text.Replace("#2", ref2);
	}
	HostRenderer.Say(ApplyNewLine(text));
	text = StripTalkSpeiclaCharacters(text); 
	text = StripTalkSpecialCharacters(text); 
	bool flag = text.StartsWith("*");
	Msg.SetColor(text.StartsWith("(") ? Msg.colors.Thinking : (flag ? Msg.colors.Ono : Msg.colors.Talk));
	if (!flag)

public void TalkRaw(string text, string ref1 = null, string ref2 = null, bool fo

cs
		Msg.Say(text.Replace("&", ""));
	}

	public string StripTalkSpeiclaCharacters(string text) 
	public string StripTalkSpecialCharacters(string text) 
	{
		switch (text[0])
		{

Chara โ€‹

public void Refresh(bool calledRecursive = false)

cs
		}
	}

	public bool CanDuplicate() 
	public bool CanDuplicate(DuplicateCondition con = DuplicateCondition.Default) 
	{
		if (EClass._zone.IsRegion || HasCondition<ConPoison>() || HasCondition<ConConfuse>() || HasCondition<ConDim>() || HasCondition<ConParalyze>() || HasCondition<ConSleep>() || HasCondition<ConBurning>() || HasCondition<ConFreeze>() || HasCondition<ConMiasma>() || corruption >= 100) 
		if (EClass._zone.IsRegion) 
		{
			return false;
		}

public bool CanDuplicate()

cs
		{
			return false;
		}
		string text = id; 
		if (!(text == "mech_scarab")) 
		if (corruption >= 100) 
		{
			if (text == "spider_queen" && (pos.cell.light > 0 || pos.IsSunLit)) 
			return false; 
		} 
		if (con != DuplicateCondition.Water && (HasCondition<ConPoison>() || HasCondition<ConConfuse>() || HasCondition<ConDim>() || HasCondition<ConParalyze>() || HasCondition<ConSleep>() || HasCondition<ConBurning>() || HasCondition<ConFreeze>() || HasCondition<ConMiasma>())) 
		{ 
			return false; 
		} 
		switch (con) 
		{ 
		case DuplicateCondition.Scarab: 
			if (isSynced || pos.cell.light > 0 || (!EClass._map.IsIndoor && !pos.cell.HasRoof && !EClass.world.date.IsNight)) 
			{
				return false;
			}
			break; 
		case DuplicateCondition.SpiderEgg: 
			if (pos.cell.light > 0 || pos.IsSunLit) 
			{ 
				return false; 
			} 
			break; 
		}
		else if (isSynced || pos.cell.light > 0 || (!EClass._map.IsIndoor && !pos.cell.HasRoof && !EClass.world.date.IsNight)) 
		return true; 
	} 
	public Chara TryDuplicate(DuplicateCondition con = DuplicateCondition.Default, Point dest = null) 
	{ 
		if (dest == null) 
		{
			return false; 
			dest = pos; 
		}
		return true; 
		dest = dest.GetRandomPoint(2, requireLos: false, allowChara: false, allowBlocked: false, 200); 
		if (dest == null || dest.Equals(pos) || !dest.IsValid || !CanDuplicate(con)) 
		{ 
			Say("split_fail", this); 
			return null; 
		} 
		Chara chara = Duplicate(); 
		EClass._zone.AddCard(chara, dest); 
		Say("split", this); 
		return chara; 
	}

	public Chara Duplicate()

public void Stumble(int mtp = 100)

cs
{
	bool flag = EClass._map.FindThing((Thing t) => t.IsInstalled && t.pos.Equals(EClass.pc.pos) && t.trait is TraitStairsUp) != null;
	Say(flag ? "dmgBurdenStairs" : "dmgBurdenFallDown", this);
	int num = MaxHP; 
	long num = MaxHP; 
	if (Evalue(1421) > 0)
	{
		num = mana.max;
	}
	int num2 = (num * (base.ChildrenWeight * 100 / WeightLimit) / (flag ? 100 : 200) + 1) * mtp / 100; 
	long num2 = (num * (base.ChildrenWeight * 100 / WeightLimit) / (flag ? 100 : 200) + 1) * mtp / 100; 
	if (base.hp <= 0)
	{
		num2 *= 2;

public void ApplyRace(bool remove = false)

cs
		}
		body.RefreshBodyParts();
		elements.ApplyElementMap(base.uid, SourceValueType.Chara, race.elementMap, base.DefaultLV, remove, applyFeat: true);
		if (!remove) 
		if (remove) 
		{
			if (race.id == "bike" && id != "bike_cub") 
			{ 
				SetFeat(1423, (id == "chara" || id == "player") ? 10 : (1 + EClass.rnd(10))); 
			} 
			if (race.id == "horse" && EClass.rnd(5) == 0) 
			if (HasElement(1423)) 
			{
				SetFeat(1423, 1 + EClass.rnd(5)); 
				SetFeat(1423, 0); 
			}
			return; 
		} 
		bool flag = id == "chara" || id == "player"; 
		if (race.id == "bike" && id != "bike_cub") 
		{ 
			SetFeat(1423, flag ? 10 : (1 + EClass.rnd(10))); 
		} 
		if (id == "moa") 
		{ 
			SetFeat(1423, flag ? 7 : (1 + EClass.rnd(7))); 
		} 
		if (race.id == "horse" && EClass.rnd(5) == 0) 
		{ 
			SetFeat(1423, flag ? 5 : (1 + EClass.rnd(5))); 
		}
	}

public string TalkTopic(string topic = "calm")

cs
	{
		return null;
	}
	string text = "_bracketTalk".lang(); 
	HashSet<string> obj = new HashSet<string> 
	{ 
		"_bracketTalk".lang(), 
		"\"", 
		"โ€œ", 
		"ใ€Œ"
	}; 
	bool flag = topicText.StartsWith("*");
	bool flag2 = topicText.StartsWith("(");
	bool flag3 = topicText.StartsWith(text) || (topicText.Length > 0 && topicText[0] == text[0]) || topicText[0] == 'โ€œ'; 
	bool flag3 = obj.Any(topicText.StartsWith); 
	topicText = ApplyTone(topicText);
	topicText = topicText.Replace("~", "*"); 
	topicText = topicText.Replace('~', '*'); 
	Msg.SetColor(flag2 ? Msg.colors.Thinking : (flag3 ? Msg.colors.Talk : Msg.colors.Ono));
	Msg.Say(StripTalkSpeiclaCharacters(topicText.Replace("&", ""))); 
	Msg.Say(StripTalkSpecialCharacters(topicText.Replace("&", ""))); 
	if (topic == "dead")
	{
		EClass.ui.popGame.PopText(ApplyNewLine(topicText.StripBrackets()), null, "PopTextDead", default(Color), pos.Position() + EClass.setting.render.tc.textPosDead);

public void RerollHobby(bool extraSlotChance = true)

cs
		string[] hobbies = source.hobbies;
		foreach (string text in hobbies)
		{
			if (EClass.sources.hobbies.alias.ContainsKey(text)) 
			if (EClass.sources.hobbies.alias.TryGetValue(text.Trim(), out var value)) 
			{
				AddHobby(EClass.sources.hobbies.alias[text].id); 
				AddHobby(value.id); 
				continue;
			}
			ModUtil.LogModError("source chara row '" + source.id + "' has invalid hobby '" + text + "'", source);

public void RerollHobby(bool extraSlotChance = true)

cs
		string[] hobbies = source.works;
		foreach (string text2 in hobbies)
		{
			if (EClass.sources.hobbies.alias.ContainsKey(text2)) 
			if (EClass.sources.hobbies.alias.TryGetValue(text2.Trim(), out var value2)) 
			{
				AddWork(EClass.sources.hobbies.alias[text2].id); 
				AddWork(value2.id); 
				continue;
			}
			ModUtil.LogModError("source chara row '" + source.id + "' has invalid work '" + text2 + "'", source);

CharaAbility โ€‹

public void Refresh()

cs
		}
		else if (!EClass.sources.elements.alias.ContainsKey(text2) || !ACT.dict.ContainsKey(text2))
		{
			if (EClass.sources.elements.fuzzyAlias.TryGetValue(text2, out var value) && ACT.dict.ContainsKey(value)) 
			if (EClass.sources.elements.fuzzyAlias.TryGetValue(text2.Trim(), out var value) && ACT.dict.ContainsKey(value)) 
			{
				list[num] = text.Replace(text2, value);
			}

ConWet โ€‹

public override void Tick()

cs

	public override void OnStart()
	{
		if (!EClass._zone.IsRegion && owner.HasElement(1254)) 
		if (!EClass._zone.IsRegion && owner.HasElement(1254) && owner.pos.ListCharasInNeighbor((Chara c) => c.HasElement(1254)).Count < 5) 
		{
			Point randomPoint = owner.pos.GetRandomPoint(2, requireLos: false, allowChara: false, allowBlocked: false, 200); 
			if (randomPoint != null && !randomPoint.Equals(BaseStats.CC.pos) && randomPoint.IsValid) 
			{ 
				Chara t = owner.Duplicate(); 
				EClass._zone.AddCard(t, randomPoint); 
				owner.Say("split", owner); 
			} 
			owner.TryDuplicate(DuplicateCondition.Water); 
		}
	}
}

ContentConfigOther โ€‹

``

cs

public class ContentConfigOther : ContentConfig
{
	public UIButton toggleSyncMod; 
	public UIButton toggleNoCensor;

	public UIButton toggleRunBackground;

public class ContentConfigOther : ContentConfig

cs

	public UIButton buttonBackerCode;

	public UIButton buttonWallPaper; 
	public UIButton toggleSyncMod; 
	public UIButton toggleDisableMods;

	public UIButton buttonWallPaper; 
	public UIButton toggleExceptionPopup; 

	public UIDropdown ddSnap;

public override void OnInstantiate()

cs
	{
		base.config.other.disableMods = on;
	});
	toggleExceptionPopup.SetToggle(base.config.other.exceptionPopup, delegate(bool on) 
	{ 
		base.config.other.exceptionPopup = on; 
	}); 
	toggleNoCensor.SetToggle(base.config.other.noCensor, delegate(bool on)
	{
		base.config.other.noCensor = on;

+ContextMenuProxy โ€‹

File Created
cs
using System;
using System.Collections.Generic;

public class ContextMenuProxy
{
	public readonly List<ContextMenuProxy> children = new List<ContextMenuProxy>();

	public Action onClick;

	public bool isMenu;

	public string DisplayName { get; }

	public string MenuEntry { get; }

	public ContextMenuProxy(string displayName, string menuEntry)
	{
		DisplayName = displayName;
		MenuEntry = menuEntry;
	}
}

Core โ€‹

public static int GetElement(string id)

cs
	{
		if (sourceElement.fuzzyAlias.TryGetValue(id, out var value2))
		{
			Debug.Log("#element lookup: " + id + " -> " + value2); 
			Debug.Log("#element lookup: '" + id + "' -> '" + value2 + "'"); 
			value = sourceElement.alias[value2];
		}
		else
		{
			Debug.LogWarning("#element not found: " + id); 
			Debug.LogWarning("#element not found: '" + id + "'"); 
			value = sourceElement.rows[0];
		}
	}

CoreConfig โ€‹

public class OtherSetting

cs

		public bool showTestOptions;

		public string idMainWidgetTheme; 
		public string idSubWidgetTheme; 
		public bool syncMods;

		public bool disableMods;

		public string idMainWidgetTheme; 
		public string idSubWidgetTheme; 
		public bool exceptionPopup; 
	}

	[Serializable]

CoreDebug โ€‹

public static string Reset_LoytelDebt()

cs
		return "Quest Reset!";
	}

	[ConsoleCommand("")] 
	public static string Fix_Awning() 
	{ 
		foreach (Thing thing in EClass._map.things) 
		{ 
			if (thing.id == "ash3") 
			{ 
				thing.id = "awning "; 
				thing.source = EClass.sources.things.map[thing.id]; 
				thing._CreateRenderer(); 
			} 
		} 
		return "Fixed!"; 
	} 
	[ConsoleCommand("")]
	public static string Fix_RemoveDuplicateUnique()
	{

CustomBiographyContent โ€‹

public class CustomBiographyContent : CustomFileContent

cs
{
	private const int FallbackWordKey = -100;

	private static int _lastWordKey; 
	private static int _lastWordKey = -101; 

	public string background;

public class CustomBiographyContent : CustomFileContent

cs

	public string favFood;

	private int[] _tempLangKey = new int[4]; 
	private bool _initialized; 
	public static CustomBiographyContent CreateFromId(string biographyId, ModPackage owner = null)
	{
		var (fileInfo, eMod) = PackageIterator.GetFilesEx("Data/bio_" + biographyId + ".json").LastOrDefault();

public override void OnSetLang(string lang)

cs
	public void RefreshCharaBio(Chara chara)
	{
		Load();
		if (_initialized) 
		{ 
			return; 
		} 
		Biography bio = chara.bio;
		bio.birthDay = birthDay; 
		bio.birthMonth = birthMonth; 
		bio.birthYear = birthYear; 
		if (birthDay != 0) 
		{ 
			bio.birthDay = birthDay; 
		} 
		if (birthMonth != 0) 
		{ 
			bio.birthMonth = birthMonth; 
		} 
		if (birthYear != 0) 
		{ 
			bio.birthYear = birthYear; 
		} 
		Dictionary<int, LangWord.Row> langWord = EClass.sources.langWord.map;
		if (!langWord.ContainsKey(-100))
		{
			_lastWordKey = -100;
			SetTempWord(""); 
			Dictionary<int, LangWord.Row> dictionary = langWord; 
			LangWord.Row obj = new LangWord.Row
			{ 
				id = -100
			}; 
			LangWord.Row row = obj; 
			dictionary[-100] = obj; 
			row.name = (row.name_JP = (row.name_L = "")); 
		}
		if (!birthPlace.IsEmpty())
		{
			bio.idHome = SetTempWord(birthPlace); 
			bio.idHome = SetTempWord(birthPlace, 0); 
		}
		if (!birthLocation.IsEmpty())
		{
			bio.idLoc = SetTempWord(birthLocation); 
			bio.idLoc = SetTempWord(birthLocation, 1); 
		}
		if (!dad.IsEmpty())
		{
			bio.idAdvDad = -100;
			bio.idDad = SetTempWord(dad); 
			bio.idDad = SetTempWord(dad, 2); 
		}
		if (!mom.IsEmpty())
		{
			bio.idAdvMom = -100;
			bio.idMom = SetTempWord(mom); 
			bio.idMom = SetTempWord(mom, 3); 
		}
		if (!likeThing.IsEmpty() && EClass.sources.things.map.ContainsKey(likeThing)) 
		if (!likeThing.IsEmpty() && EClass.sources.cards.map.ContainsKey(likeThing)) 
		{
			bio.idLike = likeThing;
		}
		if (!likeHobby.IsEmpty() && Core.GetElement(likeHobby) != EClass.sources.elements.rows[0].id) 
		if (!likeHobby.IsEmpty()) 
		{
			bio.idHobby = -100; 
			int element = Core.GetElement(likeHobby); 
			if (element != EClass.sources.elements.rows[0].id) 
			{ 
				bio.idHobby = element; 
			} 
		}
		int SetTempWord(string text) 
		_initialized = true; 
		int SetTempWord(string text, int tempIndex) 
		{
			while (langWord.ContainsKey(_lastWordKey)) 
			int num = _tempLangKey[tempIndex]; 
			if (num == 0 || !langWord.ContainsKey(num)) 
			{
				_lastWordKey--; 
				while (langWord.ContainsKey(_lastWordKey)) 
				{ 
					_lastWordKey--; 
				} 
				num = _lastWordKey; 
			}
			Dictionary<int, LangWord.Row> dictionary = langWord; 
			int lastWordKey = _lastWordKey; 
			LangWord.Row obj = new LangWord.Row
			LangWord.Row row3 = (langWord[num] = new LangWord.Row
			{
				id = _lastWordKey 
			}; 
			LangWord.Row row = obj; 
			dictionary[lastWordKey] = obj; 
			row.name = (row.name_JP = (row.name_L = text)); 
			return _lastWordKey; 
				id = num 
			}); 
			row3.name = (row3.name_JP = (row3.name_L = text)); 
			return _tempLangKey[tempIndex] = num; 
		}
	}

protected override void LoadContent()

cs
		likeHobby = customBiographyContent.likeHobby;
		favCategory = customBiographyContent.favCategory;
		favFood = customBiographyContent.favFood;
		_initialized = false; 
	}
}

CustomCharaContent โ€‹

public static SpawnType GetSpawnTypeFromTrait(string trait)

cs

	public void OnCharaCreated(Chara chara)
	{
		if (chara.GetBool("custom_content") || chara.GetBool("cwl_tags_applied") || chara.id != base.SourceId) 
		if (chara.GetBool("custom_content") || chara.id != base.SourceId) 
		{
			return;
		}

public void OnCharaCreated(Chara chara)

cs
		int value3 = value2;
		chara.SetInt(id3, value3);
	}
	if (chara.GetBool("cwl_tags_applied")) 
	{ 
		return; 
	} 
	if (things.Count > 0 || equipments.Count > 0)
	{
		chara.RemoveThings();

public override void OnGameLoad(GameIOContext context)

cs
	{
		if (num2 != 1)
		{
			goto IL_01d5; 
			goto IL_01cf; 
		}
		if (IsAdventurer)
		{
			goto IL_017e; 
			goto IL_0178; 
		}
	}
	else if (IsAdventurer)
	{
		ModUtil.LogModError("adventurer '" + base.SourceId + "' has spawned more than once", base.Owner); 
		Debug.LogWarning("#mod-content adventurer '" + base.SourceId + "' has spawned more than once"); 
		return;
	}
	if (num == 0)
	{
		goto IL_017e; 
		goto IL_0178; 
	}
	goto IL_01d5; 
	IL_017e: 
	Debug.Log("#mod-content skipping existing character '" + base.SourceId + "', " + $"{count} at {string.Join('/', list2.Select((Chara c) => c.currentZone.ZoneFullName))}"); 
	goto IL_01cf; 
	IL_0178: 
	Debug.Log("#mod-content skipping existing character '" + base.SourceId + "', " + $"{count} at {string.Join(',', list2.Select((Chara c) => c.currentZone?.ZoneFullName))}"); 
	return;
	IL_01d5: 
	IL_01cf: 
	for (int i = 0; i < num; i++)
	{
		Zone z2 = list[i];

+CustomDramaExpansion โ€‹

File Created
cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using UnityEngine;

public class CustomDramaExpansion : EClass
{
	private static readonly Dictionary<string, DramaActionParser> _actionParsers = new Dictionary<string, DramaActionParser>(StringComparer.OrdinalIgnoreCase);

	private static readonly Dictionary<string, DramaInvokeDetail> _invokes = new Dictionary<string, DramaInvokeDetail>(StringComparer.OrdinalIgnoreCase);

	internal static readonly Regex _expressionRegex = new Regex("^\\s*(?<f>\\w+)\\s*(?:\\(\\s*(?<p>.*)\\s*\\))?\\s*$", RegexOptions.Compiled);

	public static bool ParseAction(string action, DramaManager dm, Dictionary<string, string> line)
	{
		if (_actionParsers.TryGetValue(action, out var value))
		{
			return value(dm, line);
		}
		return false;
	}

	public static void AddDramaActionParser(string action, DramaActionParser parser)
	{
		_actionParsers[action] = SafeInvoke;
		Debug.Log("#drama added new action parser '" + action + "' from '" + parser.Method.TryToString() + "'");
		bool SafeInvoke(DramaManager dm, Dictionary<string, string> line)
		{
			try
			{
				return parser(dm, line);
			}
			catch (Exception ex)
			{
				ModUtil.LogModError("exception while parsing drama action '" + action + "'\n" + ex.Message, parser.Method.DeclaringType);
				Debug.LogException(ex);
			}
			return false;
		}
	}

	public static void AddDramaInvokeMethod(string name, MethodInfo method, string contract = null)
	{
		_invokes[name] = new DramaInvokeDetail(method, contract);
	}

	public static void AddDramaInvokeMethod(string name, DramaInvokeFunc func, string contract = null)
	{
		AddDramaInvokeMethod(name, func.Method, contract);
	}

	internal static (DramaInvokeDetail invoke, string[] parameters) BuildInvokeExpression(string expression)
	{
		(string, string[]) tuple = ParseInvokeExpression(expression);
		if (!tuple.Item1.IsEmpty() && _invokes.TryGetValue(tuple.Item1, out var value))
		{
			return (invoke: value, parameters: tuple.Item2);
		}
		if (expression.Length <= 1)
		{
			return (invoke: new DramaInvokeDetail(null), parameters: Array.Empty<string>());
		}
		return expression[0] switch
		{
			'!' => BuildInvokeExpression("not(" + expression[1..] + ")"), 
			'&' => BuildInvokeExpression("and(" + expression[1..] + ")"), 
			'?' => BuildInvokeExpression("or(" + expression[1..] + ")"), 
			_ => (invoke: new DramaInvokeDetail(null), parameters: Array.Empty<string>()), 
		};
	}

	internal static (string funcName, string[] parameters) ParseInvokeExpression(string expression)
	{
		if (expression.IsEmpty())
		{
			return (funcName: "", parameters: Array.Empty<string>());
		}
		Match match = _expressionRegex.Match(expression);
		return (funcName: match.Groups["f"].Value, parameters: SplitParams(match.Groups["p"].Value));
		static string[] SplitParams(string args)
		{
			if (args.IsEmpty())
			{
				return Array.Empty<string>();
			}
			List<string> list = new List<string>();
			int num = 0;
			int num2 = 0;
			char c = '\0';
			for (int i = 0; i < args.Length; i++)
			{
				char c2 = args[i];
				if (c != 0)
				{
					if (c2 == c)
					{
						c = '\0';
					}
				}
				else
				{
					switch (c2)
					{
					case '"':
					case '\'':
						c = c2;
						break;
					case '(':
						num++;
						break;
					case ')':
						num--;
						break;
					case ',':
						if (num == 0)
						{
							string s2 = args[num2..i].Trim();
							s2 = Unquote(s2);
							list.Add(s2);
							num2 = i + 1;
						}
						break;
					}
				}
			}
			if (num2 < args.Length)
			{
				string text = args[num2..].Trim();
				if (text.Length > 0)
				{
					list.Add(Unquote(text));
				}
			}
			return list.ToArray();
		}
		static string Unquote(string s)
		{
			if (s.Length >= 2 && ((s[0] == '"' && s[^1] == '"') || (s[0] == '\'' && s[^1] == '\'')))
			{
				return s[1..^1];
			}
			return s;
		}
	}

	[ElinDramaActionParser("i*")]
	[ElinDramaActionParser("invoke*")]
	internal static bool DefaultDramaInvokeExtActionParser(DramaManager dm, Dictionary<string, string> line)
	{
		string text = line["param"].Trim().RemoveNewline();
		if (text.StartsWith("//"))
		{
			return true;
		}
		var (invoke, parameters) = BuildInvokeExpression(text);
		if (invoke.Method == null)
		{
			ModUtil.LogModError("invalid drama invoke* expression '" + text + "'", new FileInfo(dm.path));
			return true;
		}
		string jump = line["jump"];
		if (!jump.IsEmpty())
		{
			dm.AddEvent(new DramaEventMethod(null)
			{
				jumpFunc = () => (!invoke.SafeInvoke(dm, line, parameters)) ? "" : jump
			});
			return true;
		}
		dm.AddEvent(new DramaEventMethod(delegate
		{
			invoke.SafeInvoke(dm, line, parameters);
		}));
		return true;
	}

	[ElinDramaActionParser("eval")]
	internal static bool DefaultDramaEvalActionParser(DramaManager dm, Dictionary<string, string> line)
	{
		string expr = line["param"];
		if (expr.IsEmpty())
		{
			return true;
		}
		EScriptSubmission submission = EScriptSubmission.Create(dm.setup.book);
		EDramaScriptState state = new EDramaScriptState
		{
			dm = dm,
			line = line
		};
		string jump = line["jump"];
		bool flag = !jump.IsEmpty();
		bool flag2 = !line["id"].IsEmpty();
		if (!flag && flag2)
		{
			Dictionary<string, string> item = new Dictionary<string, string>(line)
			{
				["action"] = "",
				["param"] = ""
			};
			dm.ParseLine(item);
			dm.lastTalk.activeCondition = delegate
			{
				object obj2 = DeferredCompileAndRun();
				return obj2 is bool && (bool)obj2;
			};
			return true;
		}
		if (flag)
		{
			dm.AddEvent(new DramaEventMethod(null)
			{
				jumpFunc = delegate
				{
					object obj = DeferredCompileAndRun();
					if (obj is string result)
					{
						return result;
					}
					return (obj is bool && !(bool)obj) ? "" : jump;
				}
			});
			return true;
		}
		dm.AddEvent(new DramaEventMethod(delegate
		{
			DeferredCompileAndRun();
		}));
		return true;
		object DeferredCompileAndRun()
		{
			if (expr.StartsWith("<<<"))
			{
				string text = expr[3..].Trim();
				string path = Path.Combine(Path.GetDirectoryName(dm.path), text);
				if (!File.Exists(path))
				{
					throw new FileNotFoundException(text);
				}
				expr = File.ReadAllText(path);
			}
			return submission.Compile<EDramaScriptState>(expr)?.Invoke(state);
		}
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[ElinDramaActionInvoke("nodiscard,passthrough")]
	public static bool eval(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		string text = string.Join(',', parameters);
		if (text.IsEmpty())
		{
			return false;
		}
		EScriptSubmission eScriptSubmission = EScriptSubmission.Create(dm.setup.book);
		EDramaScriptState arg = new EDramaScriptState
		{
			dm = dm,
			line = line
		};
		if (text.StartsWith("<<<"))
		{
			string text2 = text[3..].Trim();
			string path = Path.Combine(Path.GetDirectoryName(dm.path), text2);
			if (!File.Exists(path))
			{
				throw new FileNotFoundException(text2);
			}
			text = File.ReadAllText(path);
		}
		object obj = eScriptSubmission.Compile<EDramaScriptState>(text)?.Invoke(arg);
		if (obj is bool)
		{
			return (bool)obj;
		}
		return false;
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[ElinDramaActionInvoke("nodiscard")]
	public static bool and(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		return parameters.All(delegate(string expr)
		{
			var (dramaInvokeDetail, parameters2) = BuildInvokeExpression(expr);
			return dramaInvokeDetail.SafeInvoke(dm, line, parameters2);
		});
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[ElinDramaActionInvoke("nodiscard")]
	public static bool or(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		return parameters.Any(delegate(string expr)
		{
			var (dramaInvokeDetail, parameters2) = BuildInvokeExpression(expr);
			return dramaInvokeDetail.SafeInvoke(dm, line, parameters2);
		});
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[ElinDramaActionInvoke("nodiscard")]
	public static bool not(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		return parameters.All(delegate(string expr)
		{
			var (dramaInvokeDetail, parameters2) = BuildInvokeExpression(expr);
			return !dramaInvokeDetail.SafeInvoke(dm, line, parameters2);
		});
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	[ElinDramaActionInvoke(null)]
	public static bool console_cmd(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		string.Join(' ', parameters).EvaluateAsCommand();
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool add_item(DramaManager dm, Dictionary<string, string> line, string itemId, string materialAlias = "wood", int lv = -1, int count = 1)
	{
		Chara chara = dm.GetChara(line["actor"]);
		SourceMaterial.Row row = EClass.sources.materials.alias.TryGetValue(materialAlias, "wood");
		Thing t = ThingGen.Create(itemId, row.id, lv).SetNum(count);
		chara.Pick(t);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool apply_condition(DramaManager dm, Dictionary<string, string> line, string conditionAlias, int power = 100)
	{
		dm.GetChara(line["actor"]).AddCondition(conditionAlias, power, force: true);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool equip_item(DramaManager dm, Dictionary<string, string> line, string itemId)
	{
		dm.GetChara(line["actor"]).body.Equip(ThingGen.Create(itemId));
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool destroy_item(DramaManager dm, Dictionary<string, string> line, string itemId, int count = -1)
	{
		List<Thing> list = dm.GetChara(line["actor"]).things.List((Thing t) => t.id == itemId);
		if (count < 0)
		{
			foreach (Thing item in list)
			{
				item.Destroy();
			}
		}
		else
		{
			count = Math.Max(count, 0);
			foreach (Thing item2 in list)
			{
				if (count == 0)
				{
					break;
				}
				if (item2.Num >= count)
				{
					item2.ModNum(-count);
					continue;
				}
				count -= item2.Num;
				item2.Destroy();
			}
		}
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool remove_condition(DramaManager dm, Dictionary<string, string> line, string conditionAlias)
	{
		foreach (Condition item in dm.GetChara(line["actor"]).conditions.ToList())
		{
			if (item.source.alias == conditionAlias)
			{
				item.Kill();
			}
		}
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool join_faith(DramaManager dm, Dictionary<string, string> line, string religionId = "")
	{
		Chara chara = dm.GetChara(line["actor"]);
		if (religionId.IsEmpty())
		{
			chara.faith?.LeaveFaith(chara, EClass.game.religions.Eyth, Religion.ConvertType.Default);
		}
		else
		{
			EClass.game.religions.Find(religionId)?.JoinFaith(chara);
		}
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool join_party(DramaManager dm, Dictionary<string, string> line)
	{
		Chara chara = dm.GetChara(line["actor"]);
		EClass.Sound.Play("good");
		chara.MakeAlly();
		return true;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_affinity(DramaManager dm, Dictionary<string, string> line, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		return expr.Compare(chara._affinity);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_condition(DramaManager dm, Dictionary<string, string> line, string conditionAlias, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = ">=1";
		}
		foreach (Condition condition in dm.GetChara(line["actor"]).conditions)
		{
			if (condition.source.alias == conditionAlias)
			{
				return expr.Compare(condition.value);
			}
		}
		return false;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_currency(DramaManager dm, Dictionary<string, string> line, string currencyId, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		return expr.Compare(chara.GetCurrency(currencyId));
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_element(DramaManager dm, Dictionary<string, string> line, string elementAlias, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		if (chara.HasElement(elementAlias))
		{
			return expr.Compare(chara.Evalue(elementAlias));
		}
		return false;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_faith(DramaManager dm, Dictionary<string, string> line, string religionId, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = ">=0";
		}
		Religion faith = dm.GetChara(line["actor"]).faith;
		if (faith.id == religionId)
		{
			return expr.Compare(faith.giftRank);
		}
		return false;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_fame(DramaManager dm, Dictionary<string, string> line, DramaValueExpression expr)
	{
		return expr.Compare(EClass.player.fame);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_flag(DramaManager dm, Dictionary<string, string> line, string flagKey, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = ">=1";
		}
		Chara chara = dm.GetChara(line["actor"]);
		return expr.Compare(chara.GetInt(flagKey));
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_has_item(DramaManager dm, Dictionary<string, string> line, string itemId, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = ">=1";
		}
		Chara chara = dm.GetChara(line["actor"]);
		return expr.Compare(chara.things.List((Thing t) => t.id == itemId).Count);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_hostility(DramaManager dm, Dictionary<string, string> line, string expression)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Match match = Regex.Match(expression, "^(?<op>>=|<=|>|<|=|!=|==)?(?<h>.+)$");
		if (!match.Success)
		{
			throw new ArgumentException("invalid expression " + expression);
		}
		string value = match.Groups["op"].Value;
		if (!Enum.TryParse<Hostility>(match.Groups["h"].Value, ignoreCase: true, out var result))
		{
			throw new ArgumentException("invalid hostility " + match.Groups["h"].Value);
		}
		return new DramaValueExpression($"{value}{result:D}").Compare(chara._cints[4]);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_in_party(DramaManager dm, Dictionary<string, string> line, bool isInParty = true)
	{
		return dm.GetChara(line["actor"]).IsPCParty == isInParty;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_keyitem(DramaManager dm, Dictionary<string, string> line, string keyitemId, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = ">0";
		}
		if (EClass.sources.keyItems.alias.TryGetValue(keyitemId, out var value) && EClass.player.keyItems.TryGetValue(value.id, out var value2))
		{
			return expr.Compare(value2);
		}
		return false;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_lv(DramaManager dm, Dictionary<string, string> line, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		return expr.Compare(chara.LV);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_race(DramaManager dm, Dictionary<string, string> line, string raceId)
	{
		return dm.GetChara(line["actor"]).race.id == raceId;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_tag(DramaManager dm, Dictionary<string, string> line, string tag)
	{
		return dm.GetChara(line["actor"]).source.tag.Contains(tag);
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_zone(DramaManager dm, Dictionary<string, string> line, string zoneId, int zoneLv = 99999)
	{
		Zone currentZone = dm.GetChara(line["actor"]).currentZone;
		if (currentZone == null)
		{
			return false;
		}
		if (currentZone.id == zoneId)
		{
			if (zoneLv != 99999)
			{
				return currentZone.lv == zoneLv;
			}
			return true;
		}
		return false;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool if_zone_2(DramaManager dm, Dictionary<string, string> line, string zoneFullName)
	{
		Zone currentZone = dm.GetChara(line["actor"]).currentZone;
		Zone zone = ModUtil.FindZoneByFullName(zoneFullName);
		return currentZone == zone;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_affinity(DramaManager dm, Dictionary<string, string> line, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		chara.ModAffinity(EClass.pc, expr.Diff(chara._affinity));
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_currency(DramaManager dm, Dictionary<string, string> line, string currencyId, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		int currency = chara.GetCurrency(currencyId);
		chara.ModCurrency(expr.Diff(currency), currencyId);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_element(DramaManager dm, Dictionary<string, string> line, string elementAlias, int value = 1, int potential = 100)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Element orCreateElement = chara.elements.GetOrCreateElement(elementAlias);
		if (orCreateElement != null)
		{
			if (orCreateElement is Feat)
			{
				chara.SetFeat(orCreateElement.id, value, msg: true);
			}
			else
			{
				chara.elements.SetBase(orCreateElement.id, value, potential);
			}
		}
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_element_exp(DramaManager dm, Dictionary<string, string> line, string elementAlias, DramaValueExpression expr)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Element orCreateElement = chara.elements.GetOrCreateElement(elementAlias);
		if (orCreateElement == null)
		{
			return true;
		}
		chara.ModExp(orCreateElement.id, expr.Diff(orCreateElement.vExp));
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_fame(DramaManager dm, Dictionary<string, string> line, DramaValueExpression expr)
	{
		EClass.player.ModFame(expr.Diff(EClass.player.fame));
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool mod_flag(DramaManager dm, Dictionary<string, string> line, string flagKey, DramaValueExpression expr = null)
	{
		if (expr == null)
		{
			expr = "=1";
		}
		Chara chara = dm.GetChara(line["actor"]);
		chara.SetInt(flagKey, expr.ModOrSet(chara.GetInt(flagKey)));
		return true;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool mod_keyitem(DramaManager dm, Dictionary<string, string> line, string keyitemId, DramaValueExpression expr = null)
	{
		if (!EClass.sources.keyItems.alias.TryGetValue(keyitemId, out var value))
		{
			return false;
		}
		if (expr == null)
		{
			expr = "=1";
		}
		Dictionary<int, int> keyItems = EClass.player.keyItems;
		keyItems.TryAdd(value.id, 0);
		int num = keyItems[value.id];
		int num2 = expr.ModOrSet(keyItems[value.id]);
		if (num < num2)
		{
			SE.Play("keyitem");
			Msg.Say("get_keyItem", value.GetName());
		}
		else if (num > num2)
		{
			SE.Play("keyitem_lose");
			Msg.Say("lose_keyItem", value.GetName());
		}
		keyItems[value.id] = num2;
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool move_next_to(DramaManager dm, Dictionary<string, string> line, string charaId)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Chara chara2 = dm.GetChara(charaId);
		if (chara2 == null)
		{
			return false;
		}
		Point nearestPoint = chara2.pos.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: true, ignoreCenter: true);
		chara.TryMove(nearestPoint, allowDestroyPath: false);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool move_tile(DramaManager dm, Dictionary<string, string> line, int xOffset, int yOffset)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Point newPoint = chara.pos + new Point(xOffset, yOffset);
		chara.TryMove(newPoint, allowDestroyPath: false);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool move_to(DramaManager dm, Dictionary<string, string> line, int x, int y)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Point newPoint = new Point(x, y);
		chara.TryMove(newPoint, allowDestroyPath: false);
		return true;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool move_zone(DramaManager dm, Dictionary<string, string> line, string zoneId, int zoneLv = 99999)
	{
		Chara chara = dm.GetChara(line["actor"]);
		if (zoneLv == 99999)
		{
			zoneLv = 0;
		}
		Zone zone = ModUtil.FindZoneByFullName($"{zoneId}@{zoneLv}");
		if (zone == null)
		{
			return false;
		}
		chara.MoveZone(zone, new ZoneTransition
		{
			state = ZoneTransition.EnterState.RandomVisit
		});
		return true;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool move_zone_2(DramaManager dm, Dictionary<string, string> line, string zoneFullName)
	{
		Chara chara = dm.GetChara(line["actor"]);
		Zone zone = ModUtil.FindZoneByFullName(zoneFullName);
		if (zone == null)
		{
			return false;
		}
		chara.MoveZone(zone, new ZoneTransition
		{
			state = ZoneTransition.EnterState.RandomVisit
		});
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool play_anime(DramaManager dm, Dictionary<string, string> line, AnimeID animeId)
	{
		dm.GetChara(line["actor"]).PlayAnime(animeId, force: true);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool play_effect(DramaManager dm, Dictionary<string, string> line, string effectId)
	{
		dm.GetChara(line["actor"]).PlayEffect(effectId);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool play_effect_at(DramaManager dm, Dictionary<string, string> line, string effectId, int x, int y)
	{
		Effect.Get(effectId)?.Play(new Point(x, y));
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool play_emote(DramaManager dm, Dictionary<string, string> line, Emo emote, float duration = 1f)
	{
		dm.GetChara(line["actor"]).ShowEmo(emote, duration, skipSame: false);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool play_screen_effect(DramaManager dm, Dictionary<string, string> line, string effectId)
	{
		ScreenEffect.Play(effectId);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool pop_text(DramaManager dm, Dictionary<string, string> line, string langText)
	{
		dm.GetChara(line["actor"]).HostRenderer.Say(langText.lang());
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool set_portrait(DramaManager dm, Dictionary<string, string> line, string portraitId = null)
	{
		Person person = dm.GetPerson(line["actor"]);
		if (person == null)
		{
			return false;
		}
		string text = person.chara?.GetIdPortrait();
		if (!portraitId.IsEmpty())
		{
			string text2 = person.id.IsEmpty(person.chara?.id);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			hashSet.Add("UN_" + text2 + "_" + text + ".png");
			hashSet.Add(text + ".png");
			hashSet.Add(text2 + "_" + text + ".png");
			string text3 = hashSet.FirstOrDefault(Portrait.allIds.Contains);
			if (text3 != null)
			{
				text = text3[..^4];
			}
		}
		person.idPortrait = text;
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool set_portrait_override(DramaManager dm, Dictionary<string, string> line, string portraitId = null)
	{
		dm.GetChara(line["actor"]).SetPortraitOverride(portraitId);
		return true;
	}

	[ElinDramaActionInvoke(null)]
	public static bool set_sprite(DramaManager dm, Dictionary<string, string> line, string spriteId = null)
	{
		dm.GetChara(line["actor"]).SetSpriteOverride(spriteId);
		return true;
	}

	[ElinDramaActionInvoke("nodiscard")]
	public static bool show_book(DramaManager dm, Dictionary<string, string> line, string bookEntry)
	{
		if (BookList.dict == null)
		{
			BookList.Init();
		}
		string[] array = bookEntry.Split('/');
		string text = array[0];
		string text2 = array[1];
		if (text2.EndsWith(".txt"))
		{
			text2 = text2[..^4];
		}
		if (!BookList.dict.TryGetValue(text, out var value) || !value.TryGetValue(text2, out var value2))
		{
			return false;
		}
		bool flag = text == "Scroll";
		EClass.ui.AddLayer<LayerHelp>(flag ? "LayerParchment" : "LayerBook").book.Show((flag ? "Scroll/" : "Book/") + value2.id, null, value2.title, value2);
		return true;
	}
}

+CustomDramaExpansionHelper โ€‹

File Created
cs
using System.Collections.Generic;
using System.Linq;

public static class CustomDramaExpansionHelper
{
	public static void AddTempTalk(this DramaManager dm, string text, string actor = "tg", string? jump = null)
	{
		if (jump == null || dm.sequence.steps.ContainsKey(jump))
		{
			DramaEventTalk item = new DramaEventTalk
			{
				idActor = actor,
				idJump = jump,
				text = text,
				temp = true,
				sequence = dm.sequence
			};
			dm.sequence.tempEvents.Add(item);
		}
	}

	public static void Goto(this DramaManager dm, string step)
	{
		if (dm.sequence.steps.ContainsKey(step))
		{
			dm.sequence.Play(step);
		}
	}

	public static void InjectUniqueRumor(this DramaManager dm, Chara chara = null)
	{
		if (chara == null)
		{
			chara = dm.tg.chara;
		}
		if (chara == null)
		{
			return;
		}
		DramaCustomSequence cs = new DramaCustomSequence();
		bool flag = chara.IsHumanSpeak || EClass.pc.HasElement(1640);
		if ((!chara.IsUnique && !cs.HasTopic("unique", chara.id)) || !flag)
		{
			return;
		}
		dm.CustomEvent(dm.sequence.Exit);
		DramaChoice choice = new DramaChoice("letsTalk".lang(), dm.sequence.steps.Last().Key.IsEmpty(dm.setup.step));
		dm.lastTalk.choices.Add(choice);
		dm._choices.Add(choice);
		string rumor = (chara.IsPCParty ? chara.GetTalkText("sup") : cs.GetRumor(chara));
		choice.SetOnClick(delegate
		{
			dm.sequence.firstTalk.funcText = () => rumor;
			List<Hobby> list = chara.ListHobbies();
			Hobby hobby = ((list.Count > 0) ? list[0] : null);
			if (EClass.rnd(20) == 0 || EClass.debug.showFav)
			{
				if (EClass.rnd(2) == 0 || hobby == null)
				{
					GameLang.refDrama1 = chara.GetFavCat().GetName().ToLower();
					GameLang.refDrama2 = chara.GetFavFood().GetName();
					rumor = cs.GetText(chara, "general", "talk_fav");
					chara.knowFav = true;
				}
				else
				{
					GameLang.refDrama1 = hobby.Name.ToLower();
					rumor = cs.GetText(chara, "general", "talk_hobby");
				}
			}
			else
			{
				rumor = cs.GetRumor(chara);
			}
			chara.affinity.OnTalkRumor();
			choice.forceHighlight = true;
		}).SetCondition(() => chara.interest > 0);
	}
}

CustomFileContent โ€‹

public void Load()

cs
	{
		LoadContent();
	}
	catch (Exception message) 
	catch (Exception exception) 
	{
		ModUtil.LogModError("exception while loading file '" + base.ContentId + "'", base.Owner);
		Debug.LogError(message); 
		Debug.LogException(exception); 
	}
	finally
	{

+CustomGunEffectData โ€‹

File Created
cs
using Newtonsoft.Json;
using UnityEngine;

[JsonObject(MemberSerialization.OptOut)]
public class CustomGunEffectData : GameSetting.EffectData
{
	public bool forceLaser;

	public bool forceRail;

	public string caneColor;

	public bool caneColorBlend;

	public string idSprite = "ranged_gun";

	public string idSoundEject = "bullet_drop";

	public GameSetting.EffectData CreateEffectData()
	{
		return new CustomGunEffectData
		{
			sprite = (ModUtil.LoadSprite(idSprite) ?? Resources.Load<Sprite>("Media/Effect/General/" + idSprite)),
			eject = eject,
			firePos = firePos,
			num = num,
			delay = delay,
			idEffect = idEffect,
			idSound = idSound
		};
	}

	public static CustomGunEffectData CreateFromId(string id)
	{
		if (!EClass.setting.effect.guns.TryGetValue(id, out var value))
		{
			return null;
		}
		return new CustomGunEffectData
		{
			num = value.num,
			delay = value.delay,
			idEffect = value.idEffect,
			idSound = value.idSound,
			idSprite = (value.sprite ? value.sprite.name : ""),
			eject = value.eject,
			firePos = value.firePos
		};
	}
}

+CustomGunEffectSetting โ€‹

File Created
cs
using System.Collections.Generic;
using System.IO;

public class CustomGunEffectSetting : CustomFileContent
{
	public Dictionary<string, CustomGunEffectData> items;

	public static CustomGunEffectSetting CreateFromFile(FileInfo file, ModPackage owner = null)
	{
		if (owner == null)
		{
			owner = ModUtil.FindFileProviderPackage(file);
		}
		return new CustomGunEffectSetting
		{
			ContentId = "GunEffect/" + owner.id,
			Owner = owner,
			File = file
		};
	}

	protected override void LoadContent()
	{
		Dictionary<string, CustomGunEffectData> dictionary = IO.LoadFile<Dictionary<string, CustomGunEffectData>>(base.File.FullName, compress: false, GameIOContext.Settings);
		items = dictionary;
	}

	public override string ToString()
	{
		return $"{base.ContentId}/items({items.Count})";
	}
}

CustomMerchantStock โ€‹

public List<Thing> Generate(Card owner)

cs
			catch (Exception ex)
			{
				ModUtil.LogModError("can't create stock item '" + item2.ContentId + "'\n" + ex.Message, base.Owner);
				Debug.LogError(ex); 
				Debug.LogException(ex); 
			}
		}
		return list;
	}

	public static CustomMerchantStock CreateFromId(string stockId, ModPackage owner) 
	public static CustomMerchantStock CreateFromId(string stockId, ModPackage owner = null) 
	{
		var (fileInfo, eMod) = PackageIterator.GetFilesEx("Data/stock_" + stockId + ".json").LastOrDefault();
		if (fileInfo == null)

CustomReligionContent โ€‹

using System;

cs
using System;
using System.Collections.Generic;
using System.ComponentModel; 
using System.IO;
using System.Linq;
using System.Text;

public class CustomReligionContent : CustomSourceContent

cs

	public List<string> artifacts = new List<string>();

	[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 
	[DefaultValue(true)] 
	public bool canJoin = true;

	public bool isMinorGod;

public static void Init()

cs
		catch (Exception ex)
		{
			ModUtil.LogModError("exception while loading religion data '" + fileInfo.ShortPath() + "'\n" + ex.Message, package);
			Debug.LogError(ex); 
			Debug.LogException(ex); 
		}
	}
	LoadDeprecatedCwlSpec();

static void LoadDeprecatedCwlSpec()

cs
			catch (Exception ex2)
			{
				ModUtil.LogModError("exception while loading '" + fileInfo2.ShortPath() + "'\n" + ex2.Message, package2);
				Debug.LogError(ex2); 
				Debug.LogException(ex2); 
			}
		}
		filesEx2 = PackageIterator.GetFilesEx("Data/religion_offerings.json");

static void LoadDeprecatedCwlSpec()

cs
			catch (Exception ex3)
			{
				ModUtil.LogModError("exception while loading '" + fileInfo3.ShortPath() + "'\n" + ex3.Message, package3);
				Debug.LogError(ex3); 
				Debug.LogException(ex3); 
			}
		}
	}

internal static void LoadReligionData(GameIOContext context)

cs
	{
		if (EClass.game.religions.dictAll.TryGetValue(key, out var value))
		{
			Debug.Log($"#mod-content loading {religionCustom2.content}"); 
			Debug.Log("#mod-content loading Religion/" + value.id); 
			value.mood = religionCustom2.mood;
			value.relation = religionCustom2.relation;
			value.giftRank = religionCustom2.giftRank;

CustomThingContent โ€‹

using System.Collections.Generic;

cs
using System.Collections.Generic;
using System.ComponentModel; 
using System.Linq;
using System.Text;
using Newtonsoft.Json;

public enum SpawnType

cs
	[JsonConverter(typeof(StringEnumConverter))]
	public Rarity rarity;

	[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 
	[DefaultValue(true)] 
	public bool restock = true;

	public List<string> sockets = new List<string>();

CustomZoneContent โ€‹

using System.Collections.Generic;

cs
using System.Collections.Generic; 
using System.Linq; 
using Newtonsoft.Json;
using UnityEngine;

public class CustomZoneContent : CustomSourceContent

cs
{
	public string parent;

	public bool autoSpawn; 
	public override string SourceType => "SourceZone";

	public static CustomZoneContent CreateFromRow(SourceZone.Row r, ModPackage owner = null)
	{
		List<string> list = r.tag.Where((string t) => t.StartsWith("add")).ToList(); 
		if (list.Count == 0) 
		{ 
			return null; 
		} 
		if (owner == null)
		{
			owner = ModUtil.FindSourceRowPackage(r);

public static CustomZoneContent CreateFromRow(SourceZone.Row r, ModPackage owner

cs
		SourceId = r.id,
		Owner = owner
	};
	foreach (string item in list) 
	string[] tag = r.tag; 
	for (int i = 0; i < tag.Length; i++) 
	{
		var (text, str, _) = CustomSourceContent.GetParams(item); 
		var (text, str, _) = CustomSourceContent.GetParams(tag[i]); 
		if (text == "addMap")
		{
			customZoneContent.parent = str.IsEmpty(r.parent.IsEmpty("ntyris"));
			customZoneContent.autoSpawn = true; 
		}
	}
	return customZoneContent;

public static CustomZoneContent CreateFromRow(SourceZone.Row r, ModPackage owner

cs

	public override void OnGameLoad(GameIOContext context)
	{
		if (!autoSpawn || parent == null) 
		{ 
			return; 
		} 
		Debug.Log($"#mod-content loading {this}");
		if (EClass.game.spatials.Find(base.SourceId) != null)
		{

Dice โ€‹

public static Dice Create(Element ele, Card c)

cs

	public static Dice Create(string id, int power, Card c = null, Act act = null)
	{
		if (!EClass.sources.calc.map.ContainsKey(id)) 
		if (!EClass.sources.calc.map.TryGetValue(id, out var value) && (string.IsNullOrEmpty(act?.ID) || !EClass.sources.calc.map.TryGetValue(act.ID, out value))) 
		{
			Debug.Log(id);
			return null;
		}
		SourceCalc.Row row = EClass.sources.calc.map[id]; 
		int power2 = power;
		int ele = power / 10;
		if (act != null)

public static Dice Create(string id, int power, Card c = null, Act act = null)

cs
	}
	try
	{
		return new Dice(Mathf.Max(1, row.num.Calc(power2, ele)), Mathf.Max(1, row.sides.Calc(power2, ele)), row.bonus.Calc(power2, ele), c); 
		return new Dice(Mathf.Max(1, value.num.Calc(power2, ele)), Mathf.Max(1, value.sides.Calc(power2, ele)), value.bonus.Calc(power2, ele), c); 
	}
	catch
	{

+DramaActionParser โ€‹

File Created
cs
using System.Collections.Generic;

public delegate bool DramaActionParser(DramaManager dm, Dictionary<string, string> line);

DramaActor โ€‹

using System;

cs
using System;
using System.Collections.Generic;
using System.Reflection; 
using System.Text;

public class DramaActor : EMono

private void SetChoice(DramaChoice item, int index)

cs
		}
		if (!idAction.IsEmpty())
		{
			typeof(DramaOutcome).GetMethod(sequence.id + "_" + idAction).Invoke(sequence.manager.outcome, PARAMS); 
			if (DramaOutcome.idJump != null) 
			MethodInfo method = typeof(DramaOutcome).GetMethod(sequence.id + "_" + idAction); 
			if (method != null) 
			{
				sequence.Play(DramaOutcome.idJump); 
				return; 
				method.Invoke(sequence.manager.outcome, PARAMS); 
				if (DramaOutcome.idJump != null) 
				{ 
					sequence.Play(DramaOutcome.idJump); 
					return; 
				} 
			}
		}
		if (item.onClick != null)

DramaCustomSequence โ€‹

public void Build(Chara c)

cs
				}
				if (c.trait.ShopType != 0)
				{
					Choice2(c.trait.TextNextRestock, "_buy").DisableSound(); 
					if (!c.GetBool("disable_barter_choice")) 
					{ 
						Choice2(c.trait.TextNextRestock, "_buy").DisableSound(); 
					} 
					if (c.trait is TraitMerchantTravel && !c.IsPCFactionOrMinion)
					{
						Choice2("daRob", "_rob");

public void Build(Chara c)

cs
			{
				if (EClass.pc.HasElement(1232) || EClass.pc.HasElement(1291))
				{
					goto IL_094a; 
					goto IL_095c; 
				}
				num2 = EClass.pc.HasCondition<StanceMama>();
			}

public void Build(Chara c)

cs
			}
			if (num2)
			{
				goto IL_094a; 
				goto IL_095c; 
			}
			goto IL_095b; 
			goto IL_096d; 
		}
		if (c.isDrunk || c.HasElement(1275) || EClass.debug.enable)
		{
			Choice2(flag2 ? "daBird" : "daTail", "_tail");
		}
	}
	goto IL_0d74; 
	IL_099a: 
	Choice2("daBaby", "_baby"); 
	goto IL_09ab; 
	IL_094a: 
	goto IL_0d86; 
	IL_095c: 
	Choice2("daMama", "_mama");
	goto IL_095b; 
	IL_09ab: 
	goto IL_096d; 
	IL_09bd: 
	if (c.trait.CanRevive)
	{
		Choice2("daRevive", "_revive").DisableSound();

public void Build(Chara c)

cs
	{
		Choice2("daBlessing", "_blessing");
	}
	goto IL_0d74; 
	IL_0d74: 
	goto IL_0d86; 
	IL_09ac: 
	Choice2("daBaby", "_baby"); 
	goto IL_09bd; 
	IL_096d: 
	ConTransmuteHuman condition2 = c.GetCondition<ConTransmuteHuman>(); 
	bool num3; 
	if (condition2 == null) 
	{ 
		if (c.HasElement(1232)) 
		{ 
			goto IL_09ac; 
		} 
		num3 = c.HasElement(1291); 
	} 
	else
	{ 
		num3 = condition2.IsBaby; 
	} 
	if (num3) 
	{ 
		goto IL_09ac; 
	} 
	goto IL_09bd; 
	IL_0d86: 
	if (c.IsHomeMember())
	{
		if (c.IsMaid)

void Invest(bool quick)

cs
	End();
	Step("_end");
	End();
	return; 
	IL_095b: 
	ConTransmuteHuman condition2 = c.GetCondition<ConTransmuteHuman>(); 
	bool num3; 
	if (condition2 == null) 
	{ 
		if (c.HasElement(1232)) 
		{ 
			goto IL_099a; 
		} 
		num3 = c.HasElement(1291); 
	} 
	else
	{ 
		num3 = condition2.IsBaby; 
	} 
	if (num3) 
	{ 
		goto IL_099a; 
	} 
	goto IL_09ab; 
	void BackChill()
	{
		Method(RumorChill, null, StepDefault);

DramaEvent โ€‹

using System;

cs
using System; 
public class DramaEvent : EClass
{
	public int progress;

public class DramaEvent : EClass

cs

	public DramaSequence sequence;

	public Func<bool> activeCondition; 
	public DramaActor actor => sequence.GetActor(idActor);

	public DramaManager manager => sequence.manager;

public virtual void Reset()

cs
	{
		progress = 0;
	}
	public virtual bool CanPlay() 
	{ 
		if (activeCondition != null) 
		{ 
			return activeCondition(); 
		} 
		return true; 
	} 
}

+DramaInvokeDetail โ€‹

File Created
cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;

public class DramaInvokeDetail
{
	private Func<DramaManager, Dictionary<string, string>, string[], bool> _compiled;

	private static readonly MethodInfo _stringIsNullOrEmpty = typeof(string).GetMethod("IsNullOrEmpty", new Type[1] { typeof(string) });

	private static readonly ConstructorInfo _argumentException = typeof(ArgumentException).GetConstructor(new Type[1] { typeof(string) });

	public MethodInfo Method { get; }

	public string Contract { get; }

	public bool NoDiscard => Contract.Contains("nodiscard");

	public bool ParamPassthrough => Contract.Contains("passthrough");

	public DramaInvokeDetail(MethodInfo method, string contract = null)
	{
		Method = method;
		Contract = contract;
		ValidateSignature();
	}

	private void ValidateSignature()
	{
		if (!(Method == null))
		{
			if (Method.ContainsGenericParameters)
			{
				throw new NotSupportedException("#drama action cannot contain generic parameters");
			}
			if (!Method.IsStatic)
			{
				throw new NotSupportedException("#drama action must be static");
			}
			ParameterInfo[] parameters = Method.GetParameters();
			if (parameters.Length < 2 || parameters[0].ParameterType != typeof(DramaManager) || parameters[1].ParameterType != typeof(Dictionary<string, string>) || Method.ReturnType != typeof(bool))
			{
				throw new ArgumentException("#drama action parameters must start with 'DramaManager dm, Dictionary<string, string>' and returns 'bool'");
			}
		}
	}

	private Func<DramaManager, Dictionary<string, string>, string[], bool> BuildDelegate()
	{
		ParameterInfo[] parameters = Method.GetParameters();
		ParameterExpression parameterExpression = Expression.Parameter(typeof(DramaManager), "dm");
		ParameterExpression parameterExpression2 = Expression.Parameter(typeof(Dictionary<string, string>), "line");
		ParameterExpression parameterExpression3 = Expression.Parameter(typeof(string[]), "parameters");
		List<Expression> list = new List<Expression> { parameterExpression, parameterExpression2 };
		if (parameters.Length == 3 && parameters[2].ParameterType == typeof(string[]))
		{
			list.Add(parameterExpression3);
		}
		else if (parameters.Length > 2)
		{
			for (int i = 2; i < parameters.Length; i++)
			{
				ParameterInfo parameter = parameters[i];
				int num = i - 2;
				Expression expr = Expression.Condition(Expression.LessThan(Expression.Constant(num), Expression.ArrayLength(parameterExpression3)), Expression.ArrayIndex(parameterExpression3, Expression.Constant(num)), Expression.Constant(null, typeof(string)));
				list.Add(CreateConvertExpression(expr, parameter));
			}
		}
		return Expression.Lambda<Func<DramaManager, Dictionary<string, string>, string[], bool>>(Expression.Call(null, Method, list), new ParameterExpression[3] { parameterExpression, parameterExpression2, parameterExpression3 }).Compile();
	}

	private static Expression CreateConvertExpression(Expression expr, ParameterInfo parameter)
	{
		Type parameterType = parameter.ParameterType;
		bool isOptional = parameter.IsOptional;
		object defaultValue = parameter.DefaultValue;
		string text = $"required action parameter #{parameter.Position - 2} '{parameter.Name}'";
		if (parameterType == typeof(string))
		{
			MethodCallExpression test = Expression.Call(_stringIsNullOrEmpty, expr);
			if (isOptional)
			{
				ConstantExpression ifTrue = Expression.Constant(defaultValue, typeof(string));
				return Expression.Condition(test, ifTrue, expr);
			}
			string value = "#drama " + text + " cannot be empty";
			UnaryExpression ifTrue2 = Expression.Throw(Expression.New(_argumentException, Expression.Constant(value)), typeof(string));
			return Expression.Condition(test, ifTrue2, expr);
		}
		MethodInfo tryParseMethod = GetTryParseMethod(parameterType);
		if (tryParseMethod == null)
		{
			throw new NotSupportedException("#drama type '" + parameterType.Name + "' does not have a public static TryParse method");
		}
		ParameterExpression parameterExpression = Expression.Variable(parameterType, "parsed");
		Expression test2 = Expression.Call(tryParseMethod, expr, parameterExpression);
		Expression expression;
		if (isOptional)
		{
			expression = Expression.Constant(defaultValue, parameterType);
		}
		else
		{
			string value2 = "#drama can't parse " + text + " to type '" + parameterType.Name + "'";
			expression = Expression.Throw(Expression.New(_argumentException, Expression.Constant(value2)), parameterType);
		}
		ConditionalExpression conditionalExpression = Expression.Condition(Expression.Call(_stringIsNullOrEmpty, expr), expression, Expression.Condition(test2, parameterExpression, expression));
		return Expression.Block(new ParameterExpression[1] { parameterExpression }, conditionalExpression);
	}

	private static MethodInfo GetTryParseMethod(Type type)
	{
		if (type.IsEnum)
		{
			MethodInfo method = typeof(Enum).GetMethod("TryParse", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
			{
				typeof(string),
				type.MakeByRefType()
			}, null);
			if (method != null)
			{
				return method.MakeGenericMethod(type);
			}
		}
		return type.GetMethod("TryParse", new Type[2]
		{
			typeof(string),
			type.MakeByRefType()
		});
	}

	public bool SafeInvoke(DramaManager dm, Dictionary<string, string> line, params string[] parameters)
	{
		if (Method == null)
		{
			return true;
		}
		try
		{
			if (_compiled == null)
			{
				_compiled = BuildDelegate();
			}
			return _compiled(dm, line, parameters);
		}
		catch (Exception ex)
		{
			ModUtil.LogModError("exception while invoking drama action '" + Method.TryToString() + "'\n" + ex.Message, new FileInfo(dm.path));
			Debug.LogException(ex);
			return false;
		}
	}
}

+DramaInvokeFunc โ€‹

File Created
cs
using System.Collections.Generic;

public delegate bool DramaInvokeFunc(DramaManager dm, Dictionary<string, string> line, params string[] parameters);

DramaManager โ€‹

using System;

cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO; 
using System.Linq;
using DG.Tweening;
using UnityEngine;

public class DramaManager : EMono

cs

	public float creditSpeed;

	[NonSerialized] 
	public string path; 
	private string lastIF;

	private string lastIF2;

public class DramaManager : EMono

cs

	private string textReplace;

	public static Dictionary<string, ExcelData> dictCache = new Dictionary<string, ExcelData>(); 
	public static Dictionary<string, ExcelData> dictCache = new Dictionary<string, ExcelData>(PathComparer.Default); 

	public bool keepAmbientBGM => !bgmChanged;

	public EScriptSubmission Scripting => EScriptSubmission.Create(setup.book); 
	public DramaActor tgActor => sequence.GetActor("tg");

	private void Update()
	{
		if (sequence != null) 
		try
		{ 
			sequence?.OnUpdate(); 
		} 
		catch (Exception ex) 
		{
			sequence.OnUpdate(); 
			layer.Close(); 
			ModUtil.LogModError("error during drama play\n" + ex.GetType().Name + ": " + ex.Message, new FileInfo(path)); 
			Debug.LogException(ex); 
		}
	}

public DramaSequence Load()

cs
		sequence.AddActor("tg", tg);
	}
	sequence.AddActor("pc", new Person(EMono.pc));
	string text = PackageIterator.GetFiles("Dialog/Drama/" + setup.book + ".xlsx").LastOrDefault()?.FullName ?? (CorePath.DramaData + setup.book + ".xlsx"); 
	string text = PackageIterator.GetFiles("Dialog/Drama/" + setup.book + ".xlsx").LastOrDefault()?.FullName; 
	ExcelData excelData = dictCache.TryGetValue(text);
	if (excelData != null && excelData.IsModified())
	{

public DramaSequence Load()

cs
		excelData = new ExcelData();
	}
	excelData.maxEmptyRows = 10;
	excelData.path = text; 
	path = (excelData.path = text ?? (CorePath.DramaData + setup.book + ".xlsx")); 
	List<Dictionary<string, string>> list = excelData.BuildList(setup.sheet);
	if (!Lang.isBuiltin && dictLocalize.Count == 0) 
	dictCache[excelData.path] = excelData; 
	string key = text ?? (CorePath.DramaDataLocal + setup.book + ".xlsx"); 
	if (!File.Exists(key)) 
	{ 
		key = excelData.path; 
	} 
	ExcelData excelData2 = dictCache.TryGetValue(key); 
	if (excelData2 != excelData) 
	{ 
		if (excelData2 != null && excelData2.IsModified()) 
		{ 
			excelData2 = null; 
		} 
		if (excelData2 == null) 
		{ 
			excelData2 = new ExcelData(); 
		} 
		excelData2.maxEmptyRows = 10; 
		excelData2.path = key; 
		dictCache[excelData2.path] = excelData2; 
	} 
	dictLocalize.Clear(); 
	string[] array = new string[3] 
	{ 
		"text_" + Lang.langCode, 
		"text", 
		"text_EN"
	}; 
	foreach (Dictionary<string, string> item in excelData2.BuildList(setup.sheet)) 
	{
		foreach (Dictionary<string, string> item in new ExcelData
		string text2 = item["id"]; 
		if (text2.IsEmpty()) 
		{
			maxEmptyRows = 10, 
			path = (PackageIterator.GetFiles("Dialog/Drama/" + setup.book + ".xlsx").LastOrDefault()?.FullName ?? (CorePath.DramaDataLocal + setup.book + ".xlsx")) 
		}.BuildList(setup.sheet)) 
			continue; 
		} 
		string value = null; 
		string[] array2 = array; 
		foreach (string key2 in array2) 
		{
			string text2 = item["id"]; 
			if (!text2.IsEmpty()) 
			if (item.TryGetValue(key2, out value) && !value.IsEmpty()) 
			{
				string text3 = item.TryGetValue("text_" + Lang.langCode) ?? item["text"]; 
				if (!dictLocalize.TryAdd(text2, text3)) 
				{ 
					string text4 = dictLocalize[text2]; 
					dictLocalize[text2] = ("[DUPLICATE ID '" + text2 + "']\n").TagColor(Color.red) + text4; 
					Debug.LogError("#drama duplicate id '" + text2 + "' at " + text + "\n" + text4 + " -> " + text3); 
				} 
				break; 
			}
		}
		if (value == null) 
		{ 
			value = "[ID '" + text2 + "'] <empty text>"; 
		} 
		if (!dictLocalize.TryAdd(text2, value)) 
		{ 
			string text3 = dictLocalize[text2]; 
			dictLocalize[text2] = ("[DUPLICATE ID '" + text2 + "']\n").TagColor(Color.red) + text3; 
			Debug.LogWarning("#drama duplicate id '" + text2 + "' at " + path + "\n" + text3 + " -> " + value); 
		} 
	}
	dictCache[text] = excelData; 
	idDefault = setup.step;
	lastTalk = null;
	enableTone = (customEventsAdded = (idDefaultPassed = false));
	countLine = 0;
	for (int i = 0; i < list.Count; i++) 
	for (int j = 0; j < list.Count; j++) 
	{
		ParseLine(list[i]); 
		try
		{ 
			ParseLine(list[j]); 
		} 
		catch (Exception exception) 
		{ 
			Debug.LogException(exception); 
		} 
		countLine++;
	}
	AddCustomEvents();

public void AddCustomEvents(string idCustom = "Resident")

cs

	public void ParseLine(Dictionary<string, string> item)
	{
		string[] array = (item.ContainsKey("action") ? item["action"].Split('/') : null); 
		string action = ((array != null) ? array[0] : null); 
		string text2 = (item.ContainsKey("step") ? item["step"] : null); 
		string text2 = item.TryGetValue("step"); 
		if (text2 == "//")
		{
			return;

public void ParseLine(Dictionary<string, string> item)

cs
	{
		idDefaultPassed = true;
	}
	string actor = (item.ContainsKey("actor") ? item["actor"] : "#1"); 
	string[] p = (item.ContainsKey("param") ? item["param"].Split(',') : new string[0]); 
	string[] array = item.TryGetValue("action")?.Split('/'); 
	string action = array.TryGet(0, returnNull: true); 
	string actor = item.GetValueOrDefault("actor", "#1"); 
	string[] p = item.TryGetValue("param")?.Split(',') ?? Array.Empty<string>(); 
	string p2 = ((p.Length != 0) ? p[0] : "");
	string p3 = ((p.Length > 1) ? p[1] : "");
	string p4 = ((p.Length > 2) ? p[2] : "");
	float.TryParse(p2, out var p0f);
	float.TryParse(p3, out var result);
	bool flag = !item["text_JP"].IsEmpty(); 
	item.TryGetValue("text_JP"); 
	string text = null; 
	if (flag) 
	string text = item.TryGetValue("text_JP"); 
	bool flag = !text.IsEmpty(); 
	string id = item["id"]; 
	if (!id.IsEmpty()) 
	{
		if (!Lang.isBuiltin) 
		{ 
			string key = item["id"]; 
			if (dictLocalize.ContainsKey(key)) 
			{ 
				text = dictLocalize[key]; 
			} 
			else
			{ 
				text = item.TryGetValue("text_EN"); 
			} 
		} 
		else
		{ 
			text = item["text_" + Lang.langCode]; 
		} 
		text = dictLocalize.TryGetValue(id); 
		flag = !text.IsEmpty(); 
	}
	if (flag && text.StartsWith("$") && tg != null && tg.hasChara)
	{
		string text3 = text.Split(' ')[0];
		text = text.Replace(text3, tg.chara.GetTalkText(text3.Remove(0, 1))); 
		text = text.Replace(text3, tg.chara.GetTalkText(text3[1..])); 
	}
	string jump = (item.ContainsKey("jump") ? item["jump"] : null); 
	string text4 = (item.ContainsKey("if") ? item["if"] : null); 
	string iF = (item.ContainsKey("if2") ? item["if2"] : null); 
	string cHECK = (item.ContainsKey("check") ? item["check"] : null); 
	string jump = item.TryGetValue("jump"); 
	string text4 = item.TryGetValue("if"); 
	string iF = item.TryGetValue("if2"); 
	string cHECK = item.TryGetValue("check"); 
	bool flag2 = false;
	if (text2 != null && !sequence.steps.ContainsKey(text2) && action != "choice" && action != "cancel") 
	if (!text2.IsEmpty() && !sequence.steps.ContainsKey(text2) && action != "choice" && action != "cancel") 
	{
		sequence.steps.Add(text2, sequence.events.Count);
	}

public void ParseLine(Dictionary<string, string> item)

cs
	{
		if (action == "reload")
		{
			string id = "flag" + countLine; 
			sequence.AddStep(id); 
			string id2 = "flag" + countLine; 
			sequence.AddStep(id2); 
		}
		return;
	}

public void ParseLine(Dictionary<string, string> item)

cs
		}
		break;
	case "choice":
		if (!CheckIF(text4) || !CheckIF(iF)) 
		{ 
			break; 
		} 
	{ 
		if (array.Length > 1)
		{
			switch (array[1])

public void ParseLine(Dictionary<string, string> item)

cs
			}
		}
		flag2 = true;
		lastTalk.AddChoice(new DramaChoice(text, jump, p2, cHECK, text4)); 
		DramaChoice dramaChoice = new DramaChoice(text, jump, p2, cHECK, text4); 
		lastTalk.AddChoice(dramaChoice); 
		var (invoke, invokeP) = CustomDramaExpansion.BuildInvokeExpression(item.TryGetValue("param")); 
		if (invoke.Method != null) 
		{ 
			dramaChoice.idAction = ""; 
			dramaChoice.SetCondition(() => invoke.SafeInvoke(this, item, invokeP)); 
		} 
		break;
	} 
	case "addTempActor":
	{
		Person person = new Person(actor);

public void ParseLine(Dictionary<string, string> item)

cs
			else
			{
				imageBG.enabled = true;
				imageBG.sprite = Resources.Load<Sprite>("Media/Graphics/Image/Drama/" + p2); 
				imageBG.sprite = Resources.Load<Sprite>("Media/Graphics/Image/Drama/" + p2) ?? ModUtil.LoadSprite(p2); 
			}
		});
		break;

public void ParseLine(Dictionary<string, string> item)

cs
			else
			{
				dialog.imageBgAdv.enabled = true;
				dialog.imageBgAdv.sprite = Resources.Load<Sprite>("Media/Graphics/Image/Drama/" + p2); 
				dialog.imageBgAdv.sprite = Resources.Load<Sprite>("Media/Graphics/Image/Drama/" + p2) ?? ModUtil.LoadSprite(p2); 
			}
		});
		break;

public void ParseLine(Dictionary<string, string> item)

cs
		});
		break;
	default:
	{ 
		EVENT.ElinDramaParseActionEventArgs elinDramaParseActionEventArgs = new EVENT.ElinDramaParseActionEventArgs
		{ 
			dm = this, 
			line = item 
		}; 
		elinDramaParseActionEventArgs.SetData(action); 
		BaseModManager.PublishEvent("elin.drama.parse_action", elinDramaParseActionEventArgs); 
		if (elinDramaParseActionEventArgs.IsUsed) 
		if (CustomDramaExpansion.ParseAction(action, this, item)) 
		{
			return;
		}

public void ParseLine(Dictionary<string, string> item)

cs
				text = textReplace;
				textReplace = null;
			}
			if (text.StartsWith("#eval")) 
			{ 
				string script = text[5..].Trim(); 
				if (!EScript.IsScriptingAvailable) 
				{ 
					return ("[ID '" + id + "'] <scripting is disabled>").TagColor(Color.red); 
				} 
				Func<EDramaScriptState, object> func = Scripting.Compile<EDramaScriptState>(script); 
				EDramaScriptState arg = new EDramaScriptState
				{ 
					dm = this, 
					line = item 
				}; 
				text = func(arg).TryToString("[ID '" + id + "'] <scripting returns no text>"); 
			} 
			if (tg != null && (actor == "tg" || actor.IsEmpty()))
			{
				text = (enableTone ? tg.ApplyTone(text) : text);

public void ParseLine(Dictionary<string, string> item)

cs
		})) as DramaEventTalk;
		lastTalk.center = p2 == "center";
		break;
	} 
	case "new":
	case "saveBGM":
	case "checkAffinity":

public void ParseLine(Dictionary<string, string> item)

cs
		}
	}

	public Chara GetChara(string id) 
	{ 
		return GetPerson(id)?.chara; 
	} 
	public Person GetPerson(string id) 
	{ 
		return GetActor(id)?.owner; 
	} 
	public DramaActor GetActor(string id)
	{
		return sequence.GetActor(id);

DramaSequence โ€‹

public void Clear()

cs

	public DramaActor GetActor(string id)
	{
		if (actors.ContainsKey(id)) 
		if (actors.TryGetValue(id, out var value)) 
		{
			return actors[id]; 
			return value; 
		}
		if (EClass.sources.persons.map.ContainsKey(id))
		{

public DramaActor GetActor(string id)

cs
			return AddActor(id, new Person(chara));
		}
	}
	if (EClass.sources.charas.map.TryGetValue(id, out var value2)) 
	{ 
		Person person = new Person(id) 
		{ 
			name = value2.GetName() 
		}; 
		if (Portrait.allIds.Contains(person.id)) 
		{ 
			person.idPortrait = "UN_" + id; 
		} 
		return AddActor(id, person); 
	} 
	if (actors.Count <= 0)
	{
		return GetActor("narrator");

public T GetEvent<T>(string idStep) where T : DramaEvent

cs

	public DramaActor AddActor(string id, Person person)
	{
		if (actors.ContainsKey(id)) 
		if (actors.TryGetValue(id, out var value)) 
		{
			return actors[id]; 
			return value; 
		}
		DramaActor dramaActor = Util.Instantiate(manager.moldActor, manager.actorPos); 
		dramaActor.Init(this, id, person); 
		actors.Add(id, dramaActor); 
		return dramaActor; 
		value = Util.Instantiate(manager.moldActor, manager.actorPos); 
		value.Init(this, id, person); 
		actors.Add(id, value); 
		return value; 
	}

	public void AddStep(string id)

public void OnUpdate()

cs
			}
			Play(text);
		}
		else if (currentEvent.Play()) 
		else if (!currentEvent.CanPlay() || currentEvent.Play()) 
		{
			PlayNext();
		}

+DramaValueExpression โ€‹

File Created
cs
using System.Text.RegularExpressions;
using UnityEngine;

public class DramaValueExpression
{
	private static readonly Regex _listSyntaxRegex = new Regex("^(?<op>>=|<=|==|!=|--|\\+\\+|[><=+\\-*/x])?(?<number>.*)$", RegexOptions.Compiled);

	public string Expression { get; }

	public string Op { get; }

	public string Rhs { get; }

	public DramaValueExpression(string expression)
	{
		Expression = expression.Trim();
		Match match = _listSyntaxRegex.Match(Expression);
		Op = match.Groups["op"].Value;
		Rhs = match.Groups["number"].Value;
	}

	public static bool TryParse(string expression, out DramaValueExpression valueExpr)
	{
		valueExpr = new DramaValueExpression(expression);
		return true;
	}

	public static implicit operator DramaValueExpression(string expr)
	{
		return new DramaValueExpression(expr);
	}

	public static implicit operator string(DramaValueExpression expr)
	{
		return expr.Expression;
	}

	public int Calc(int lhs)
	{
		if (Expression.IsEmpty())
		{
			return lhs;
		}
		if (!$"{lhs} {Expression}".TryEvaluateAsCalc(out int result, (object)null))
		{
			return lhs;
		}
		return result;
	}

	public float Calc(float lhs)
	{
		if (Expression.IsEmpty())
		{
			return lhs;
		}
		if (!$"{lhs} {Expression}".TryEvaluateAsCalc(out float result, (object)null))
		{
			return lhs;
		}
		return result;
	}

	public float Diff(float lhs)
	{
		return ModOrSet(lhs) - lhs;
	}

	public int Diff(int lhs)
	{
		return ModOrSet(lhs) - lhs;
	}

	public float ModOrSet(float lhs)
	{
		if (Expression.IsEmpty())
		{
			return lhs;
		}
		string op = Op;
		if (!(op == "++"))
		{
			if (op == "--")
			{
				return lhs - 1f;
			}
			if (!float.TryParse(Rhs, out var result))
			{
				return lhs;
			}
			switch (Op)
			{
			case "+":
				return lhs + result;
			case "-":
				return lhs - result;
			case "*":
			case "x":
				return lhs * result;
			case "/":
				return lhs / result;
			case "=":
			case "==":
				return result;
			case "":
				return result;
			default:
				return lhs;
			}
		}
		return lhs + 1f;
	}

	public int ModOrSet(int lhs)
	{
		if (Expression.IsEmpty())
		{
			return lhs;
		}
		string op = Op;
		if (!(op == "++"))
		{
			if (op == "--")
			{
				return lhs - 1;
			}
			if (!int.TryParse(Rhs, out var result))
			{
				return lhs;
			}
			switch (Op)
			{
			case "+":
				return lhs + result;
			case "-":
				return lhs - result;
			case "*":
			case "x":
				return lhs * result;
			case "/":
				return lhs / result;
			case "=":
			case "==":
				return result;
			case "":
				return result;
			default:
				return lhs;
			}
		}
		return lhs + 1;
	}

	public bool Compare(float lhs)
	{
		if (Expression.IsEmpty())
		{
			return false;
		}
		if (Op == "")
		{
			if (float.TryParse(Rhs, out var result))
			{
				return Mathf.Approximately(lhs, result);
			}
			return false;
		}
		if (!float.TryParse(Rhs, out var result2))
		{
			return false;
		}
		switch (Op)
		{
		case ">":
			return lhs > result2;
		case "<":
			return lhs < result2;
		case "=":
		case "==":
			return Mathf.Approximately(lhs, result2);
		case ">=":
			return lhs >= result2;
		case "<=":
			return lhs <= result2;
		case "!=":
			return !Mathf.Approximately(lhs, result2);
		default:
			return false;
		}
	}

	public bool Compare(int lhs)
	{
		if (Expression.IsEmpty())
		{
			return false;
		}
		if (Op == "")
		{
			if (int.TryParse(Rhs, out var result))
			{
				return lhs == result;
			}
			return false;
		}
		if (!int.TryParse(Rhs, out var result2))
		{
			return false;
		}
		switch (Op)
		{
		case ">":
			return lhs > result2;
		case "<":
			return lhs < result2;
		case "=":
		case "==":
			return lhs == result2;
		case ">=":
			return lhs >= result2;
		case "<=":
			return lhs <= result2;
		case "!=":
			return lhs != result2;
		default:
			return false;
		}
	}
}

+DuplicateCondition โ€‹

File Created
cs
public enum DuplicateCondition
{
	Default,
	Scarab,
	SpiderEgg,
	Water
}

+EDramaScriptState โ€‹

File Created
cs
using System.Collections.Generic;

public class EDramaScriptState : EScriptState
{
	public DramaManager dm;

	public Dictionary<string, string> line;

	public Chara pc => EClass.pc;

	public Chara tg => dm.GetActor("tg").owner.chara;

	public string text
	{
		get
		{
			return line["text"];
		}
		set
		{
			line["text"] = value;
		}
	}
}

+ElinActPerformAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinActPerformAttribute : ElinEventBaseAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<Act> handler = method.CreateDelegate<Action<Act>>();
		BaseModManager.SubscribeEvent("elin.act_performed", handler);
	}
}

+ElinCharaOnCreateAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinCharaOnCreateAttribute : ElinEventBaseAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<Chara> handler = method.CreateDelegate<Action<Chara>>();
		BaseModManager.SubscribeEvent("elin.chara_created", handler);
	}
}

+ElinContextMenuEntryAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinContextMenuEntryAttribute : ElinEventBaseAttribute
{
	public string DisplayName { get; }

	public string MenuEntry { get; }

	public ElinContextMenuEntryAttribute(string langEntry, string langDisplay = "")
	{
		MenuEntry = langEntry.lang();
		DisplayName = langDisplay.IsEmpty(MenuEntry.Split('/')[^1]).lang();
	}

	public override void Register(MethodInfo method)
	{
		ModUtil.AddContextMenuEntry(method.CreateDelegate<Action>(), MenuEntry, DisplayName);
	}
}

+ElinDramaActionInvokeAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinDramaActionInvokeAttribute : ElinEventBaseAttribute
{
	public string Contract { get; }

	public ElinDramaActionInvokeAttribute(string contract = null)
	{
		Contract = contract;
	}

	public override void Register(MethodInfo method)
	{
		CustomDramaExpansion.AddDramaInvokeMethod(method.Name, method, Contract);
	}
}

+ElinDramaActionParserAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class ElinDramaActionParserAttribute : ElinEventBaseAttribute
{
	public string DramaAction { get; }

	public ElinDramaActionParserAttribute(string action)
	{
		if (action.IsEmpty())
		{
			throw new ArgumentNullException("action");
		}
		DramaAction = action;
	}

	public override void Register(MethodInfo method)
	{
		DramaActionParser parser = method.CreateDelegate<DramaActionParser>();
		CustomDramaExpansion.AddDramaActionParser(DramaAction, parser);
	}
}

+ElinEventBaseAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

public abstract class ElinEventBaseAttribute : Attribute
{
	public virtual void Register(MethodInfo method)
	{
	}

	public virtual void Register(PropertyInfo property)
	{
	}
}

+ElinGameIOEventAttribute โ€‹

File Created
cs
using System;

[AttributeUsage(AttributeTargets.Method)]
public abstract class ElinGameIOEventAttribute : ElinEventBaseAttribute
{
}

+ElinGameIOPropertyAttribute โ€‹

File Created
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using UnityEngine;

[AttributeUsage(AttributeTargets.Property)]
public class ElinGameIOPropertyAttribute : ElinGameIOEventAttribute
{
	private static bool _registered;

	private static readonly Dictionary<string, (PropertyInfo property, Func<object> getter, Action<object> setter)> _contextVars = new Dictionary<string, (PropertyInfo, Func<object>, Action<object>)>();

	public string ChunkName { get; }

	public ElinGameIOPropertyAttribute(string chunkName)
	{
		ChunkName = chunkName;
	}

	public override void Register(PropertyInfo property)
	{
		if (!_registered)
		{
			BaseModManager.SubscribeEvent<GameIOContext>("elin.game.post_load", LoadGameIOProperty);
			BaseModManager.SubscribeEvent<GameIOContext>("elin.game.post_save", SaveGameIOProperty);
			_registered = true;
		}
		MethodInfo getMethod = property.GetMethod;
		MethodInfo setMethod = property.SetMethod;
		if (!(getMethod == null) && !(setMethod == null) && setMethod.IsStatic)
		{
			Func<object> item = getMethod.CreateDelegate<Func<object>>();
			Action<object> item2 = CreateSetterDelegate(setMethod);
			Type declaringType = property.DeclaringType;
			_contextVars[declaringType.FullName + ":" + ChunkName] = (property, item, item2);
		}
	}

	private static Action<object> CreateSetterDelegate(MethodInfo method)
	{
		Type parameterType = method.GetParameters()[0].ParameterType;
		ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "obj");
		UnaryExpression arg = Expression.Convert(parameterExpression, parameterType);
		return Expression.Lambda<Action<object>>(Expression.Call(method, arg), new ParameterExpression[1] { parameterExpression }).Compile();
	}

	private static void LoadGameIOProperty(GameIOContext context)
	{
		if (!context.Load<Dictionary<string, object>>("context_vars", out var data))
		{
			data = new Dictionary<string, object>();
		}
		KeyValuePair<string, (PropertyInfo, Func<object>, Action<object>)>[] array = _contextVars.ToArray();
		foreach (KeyValuePair<string, (PropertyInfo, Func<object>, Action<object>)> keyValuePair in array)
		{
			keyValuePair.Deconstruct(out var key, out var value);
			(PropertyInfo, Func<object>, Action<object>) tuple = value;
			string text = key;
			var (propertyInfo, _, action) = tuple;
			try
			{
				string key2 = propertyInfo.DeclaringType.FullName + ":" + text;
				object valueOrDefault = data.GetValueOrDefault(key2);
				action(valueOrDefault);
			}
			catch (Exception arg)
			{
				Debug.LogError($"#io failed to populate context var {text}\n{arg}");
				_contextVars.Remove(text);
			}
		}
	}

	private static void SaveGameIOProperty(GameIOContext context)
	{
		Dictionary<string, object> dictionary = new Dictionary<string, object>(StringComparer.Ordinal);
		KeyValuePair<string, (PropertyInfo, Func<object>, Action<object>)>[] array = _contextVars.ToArray();
		foreach (KeyValuePair<string, (PropertyInfo, Func<object>, Action<object>)> keyValuePair in array)
		{
			keyValuePair.Deconstruct(out var key, out var value);
			(PropertyInfo, Func<object>, Action<object>) tuple = value;
			string text = key;
			var (propertyInfo, func, _) = tuple;
			try
			{
				object value2 = func();
				string key2 = propertyInfo.DeclaringType.FullName + ":" + text;
				dictionary[key2] = value2;
			}
			catch (Exception arg)
			{
				Debug.LogError($"#io failed to save context var {text}\n{arg}");
				_contextVars.Remove(text);
			}
		}
		context.Save("context_vars", dictionary);
	}
}

+ElinPostLoadAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

public sealed class ElinPostLoadAttribute : ElinGameIOEventAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<GameIOContext> handler = method.CreateDelegate<Action<GameIOContext>>();
		BaseModManager.SubscribeEvent("elin.game.start_new", handler);
		BaseModManager.SubscribeEvent("elin.game.post_load", handler);
	}
}

+ElinPostSaveAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

public sealed class ElinPostSaveAttribute : ElinGameIOEventAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<GameIOContext> handler = method.CreateDelegate<Action<GameIOContext>>();
		BaseModManager.SubscribeEvent("elin.game.post_save", handler);
	}
}

+ElinPostSceneInitAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinPostSceneInitAttribute : ElinEventBaseAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<Scene.Mode> handler = method.CreateDelegate<Action<Scene.Mode>>();
		BaseModManager.SubscribeEvent("elin.scene.post_init", handler);
	}
}

+ElinPreLoadAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

public sealed class ElinPreLoadAttribute : ElinGameIOEventAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<GameIOContext> handler = method.CreateDelegate<Action<GameIOContext>>();
		BaseModManager.SubscribeEvent("elin.game.pre_load", handler);
	}
}

+ElinPreSaveAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

public sealed class ElinPreSaveAttribute : ElinGameIOEventAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<GameIOContext> handler = method.CreateDelegate<Action<GameIOContext>>();
		BaseModManager.SubscribeEvent("elin.game.pre_save", handler);
	}
}

+ElinPreSceneInitAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinPreSceneInitAttribute : ElinEventBaseAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<Scene.Mode> handler = method.CreateDelegate<Action<Scene.Mode>>();
		BaseModManager.SubscribeEvent("elin.scene.pre_init", handler);
	}
}

+ElinThingOnCreateAttribute โ€‹

File Created
cs
using System;
using System.Reflection;

[AttributeUsage(AttributeTargets.Method)]
public sealed class ElinThingOnCreateAttribute : ElinEventBaseAttribute
{
	public override void Register(MethodInfo method)
	{
		Action<Thing> handler = method.CreateDelegate<Action<Thing>>();
		BaseModManager.SubscribeEvent("elin.thing_created", handler);
	}
}

FACTION โ€‹

public class Faction : EClass

cs

	public static Faction Create(SourceFaction.Row r)
	{
		Faction faction = ClassCache.Create<Faction>(r.type, "Elin"); 
		faction.id = r.id; 
		faction.Init(); 
		return faction; 
		Faction obj = ClassCache.Create<Faction>(r.type, "Elin") ?? new Faction(); 
		obj.id = r.id; 
		obj.Init(); 
		return obj; 
	}

	public void Init()

FEAT โ€‹

public override void WritePurchaseReq(UINote n, int lv = -1)

cs
		}
	}

	public List<string> Apply(int a, ElementContainer owner, bool hint = false) 
	public virtual List<string> Apply(int a, ElementContainer owner, bool hint = false) 
	{
		if (hint)
		{

HotItemContext โ€‹

public static void Show(string id, Vector3 pos)

cs
			{
				Util.ShowExplorer(CorePath.custom + "Portrait");
			});
			if (ModUtil.contextMenuProxies.Count > 0) 
			{ 
				UIContextMenu parent2 = i.AddChild("mod"); 
				foreach (ContextMenuProxy contextMenuProxy in ModUtil.contextMenuProxies) 
				{ 
					PopulateMenu(parent2, contextMenuProxy); 
				} 
			} 
			i.AddSeparator();
			i.AddButton("help", delegate
			{

void AddTilt()

cs
			EClass.scene.camSupport.tiltShift.blurArea = 0.1f * b;
		}, 0f, 150f, isInt: true, hideOther: false);
	}
	static void PopulateMenu(UIContextMenu parent, ContextMenuProxy proxy) 
	{ 
		if (proxy.isMenu) 
		{ 
			UIContextMenu parent3 = parent.AddChild(proxy.DisplayName); 
			{ 
				foreach (ContextMenuProxy child in proxy.children) 
				{ 
					PopulateMenu(parent3, child); 
				} 
				return; 
			} 
		} 
		parent.AddButton(proxy.DisplayName, proxy.onClick); 
	} 
	static void Toggle(ref bool flag)
	{
		flag = !flag;

ModManager โ€‹

public class ModManager : ModManagerCore

cs

	public List<FileInfo> replaceFiles = new List<FileInfo>();

	private Action ImportModGodTalks; 
	public static List<string> ListChainLoad => BaseModManager.listChainLoad;

	public static DirectoryInfo DirWorkshop => Instance.dirWorkshop;

public override void Init(string path, string defaultPackage = "_Elona")

cs
			Core.SaveElinIni(elinIni);
		}
	}
	if (!dirWorkshop.Exists) 
	if ((!(dirWorkshop?.Exists)) ?? true) 
	{
		dirWorkshop = null;
	}

ModUtil โ€‹

using NPOI.SS.UserModel;

cs
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using Newtonsoft.Json;
using ReflexCLI; 
using ReflexCLI.Attributes;
using UnityEngine;
using UnityEngine.Networking;

public class ModUtil : EClass

cs

	private static readonly List<Func<string, object, int>> _customCalcEvaluator = new List<Func<string, object, int>>();

	private static readonly HashSet<Type> _checkedAttributedTypes = new HashSet<Type>(); 
	private static readonly Dictionary<string, Texture2D> _cachedTextures = new Dictionary<string, Texture2D>();

	private static Effect _rodTemplate; 
	public static SourceImporter sourceImporter = new SourceImporter(SourceMapping);

	public static readonly List<ContextMenuProxy> contextMenuProxies = new List<ContextMenuProxy>(); 
	public static IReadOnlyDictionary<string, SourceData> SourceMapping => (from f in typeof(SourceManager).GetFields()
		where typeof(SourceData).IsAssignableFrom(f.FieldType)
		select f).ToDictionary((FieldInfo f) => f.FieldType.Name, (FieldInfo f) => f.GetValue(EClass.sources) as SourceData);

public static void RegisterSerializedTypeFallback(string nameAssembly, string na

cs

	public static void LogModError(string message, BaseModPackage package = null)
	{
		string text = "#mod/" + package?.id + "\n" + message; 
		string text = "#mod/" + package?.title + " (" + package?.id + ")\n" + message; 
		UnityEngine.Debug.LogWarning(text.RemoveAllTags());
		if ((package?.isInPackages ?? false) || Application.isEditor)
		{

public static void LogModError(string message, BaseModPackage package = null)

cs
		}
	}

	public static void LogModError(string message, SourceData.BaseRow row = null) 
	public static void LogModError(string message, SourceData.BaseRow row) 
	{
		LogModError(message, FindSourceRowPackage(row));
	}

	public static void LogModError(string message, FileInfo file = null) 
	public static void LogModError(string message, Type type) 
	{ 
		LogModError(message, FindFileProviderPackage(new FileInfo(type.Assembly.Location))); 
	} 
	public static void LogModError(string message, FileInfo file) 
	{
		LogModError(message, FindFileProviderPackage(file));
	}

	public static void LogModError(string message, DirectoryInfo dir = null) 
	public static void LogModError(string message, DirectoryInfo dir) 
	{
		LogModError(message, FindDirectoryProviderPackage(dir));
	}

public static ModPackage GetModPackage(string modId)

cs
	public static ModPackage FindFileProviderPackage(FileInfo file)
	{
		string path = file.FullName.NormalizePath();
		return ModManager.Instance.packages.LastOrDefault((BaseModPackage p) => path.StartsWith(p.dirInfo.FullName)) as ModPackage; 
		return ModManager.Instance.packages.LastOrDefault((BaseModPackage p) => path.StartsWith(p.dirInfo.FullName.NormalizePath())) as ModPackage; 
	}

	public static ModPackage FindDirectoryProviderPackage(DirectoryInfo dir)
	{
		string path = dir.FullName.NormalizePath();
		return ModManager.Instance.packages.LastOrDefault((BaseModPackage p) => path.StartsWith(p.dirInfo.FullName)) as ModPackage; 
		return ModManager.Instance.packages.LastOrDefault((BaseModPackage p) => path.StartsWith(p.dirInfo.FullName.NormalizePath())) as ModPackage; 
	}

	public static ModPackage FindSourceRowPackage(SourceData.BaseRow row)

where z.CanSpawnAdv

cs

	public static void OnModsActivated()
	{
		_cachedTextures.Clear(); 
		_customContent.Clear(); 
		CommandRegistry.assemblies.Add(typeof(EScript).Assembly); 
		SoundManager.current.soundLoaders.Add(LoadSoundData);
		UIBook.topicLoaders.Add(LoadTopicFiles);
		BookList.booklistLoaders.Add(LoadBookList);
		Lang.excelDialogLoaders.Add(LoadExcelDialog);
		BaseModManager.SubscribeEvent<GameIOContext>("elin.game.post_load", OnPostLoadInit); 
		BaseModManager.SubscribeEvent<GameIOContext>("elin.game.start_new", OnPostLoadInit); 
		BaseModManager.SubscribeEvent<GameIOContext>("elin.game.post_save", OnPostSaveInit); 
		LoadEffectTemplate(); 
		DynamicAsset<Effect>.assetLoaders.Add(LoadEffect); 
		BaseModManager.SubscribeEvent<string>("elin.source.lang_set", OnSetLang);
		BaseModManager.SubscribeEvent<Chara>("elin.chara_created", OnCharaCreated); 
		BaseModManager.SubscribeEvent<Thing>("elin.thing_created", OnThingCreated); 
		BaseModManager.SubscribeEvent<List<Religion>>("elin.religion_importing", OnReligionImporting);
		BaseModManager.SubscribeEvent<Act>("elin.act_performed", OnActPerformed); 
		BaseModManager.SubscribeEvent("elin.source.importing", OnSourceImporting);
		BaseModManager.SubscribeEvent("elin.source.imported", OnSourceImported);
		BaseModManager.PublishEvent("elin.mods.activated");
		RegisterElinEventAttributes(); 
		CoroutineHelper.Deferred(RegisterElinEventAttributes); 
	}

	private static void OnSourceImporting()
	{
		if (ModManagerCore.enableSheetLoading) 
		{ 
			ImportAllModSourceSheets(); 
		} 
		ImportAllModSourceSheets(); 
	}

	private static void OnSourceImported()

private static void OnSourceImported()

cs
			catch (Exception ex)
			{
				LogModError("exception while generating custom source profiles\n" + ex.Message, activatedUserMod);
				UnityEngine.Debug.LogError(ex); 
				UnityEngine.Debug.LogException(ex); 
			}
		}
		ImportAllGunEffectSettings(); 
		CustomReligionContent.managed.Clear();
		foreach (CustomReligionContent item2 in _customContent.Values.OfType<CustomReligionContent>())
		{
			item2.RegisterCustomReligion();
		}
		CustomReligionContent.Init();
		SourceCache.FinalizeCache(); 
		SourceCache.InvalidateCacheBlobs(); 
		SourceCache.ClearDetail(); 
		if (EClass.core.launchArgs.Contains("EXPORTSOURCE")) 
		{ 
			string text = CorePath.rootExe + "/SourceExport/" + EClass.core.version.GetText(); 
			ExportAllSourceDataCsv(text); 
			UnityEngine.Debug.Log("#source exported current version to " + text); 
		} 
	}

	[ElinPreLoad] 
	private static void OnPreLoadInit(GameIOContext context) 
	{ 
	} 
	[ElinPostLoad] 
	private static void OnPostLoadInit(GameIOContext context)
	{
		EClass.player.knownBGMs.RemoveWhere((int id) => !EClass.core.refs.dictBGM.ContainsKey(id));

void LoadCustomContent<T>() where T : class, ICustomContent

cs
			catch (Exception ex2)
			{
				LogModError("exception while loading custom content '" + item.ContentId + "'\n" + ex2.Message, item.Owner);
				UnityEngine.Debug.LogError(ex2); 
				UnityEngine.Debug.LogException(ex2); 
			}
		}
	}

void LoadOtherCustomContent()

cs
				catch (Exception ex)
				{
					LogModError("exception while loading custom content '" + item2.ContentId + "'\n" + ex.Message, item2.Owner);
					UnityEngine.Debug.LogError(ex); 
					UnityEngine.Debug.LogException(ex); 
				}
			}
		}
	}

	[ElinPreSave] 
	private static void OnPreSaveInit(GameIOContext context) 
	{ 
	} 
	[ElinPostSave] 
	private static void OnPostSaveInit(GameIOContext context)
	{
		CustomReligionContent.SaveReligionData(context);

private static void OnPostSaveInit(GameIOContext context)

cs
		catch (Exception ex)
		{
			LogModError("exception while saving custom content '" + value.ContentId + "'\n" + ex.Message, value.Owner);
			UnityEngine.Debug.LogError(ex); 
			UnityEngine.Debug.LogException(ex); 
		}
	}
}

private static void OnReligionImporting(List<Religion> list)

cs
		list.AddRange(CustomReligionContent.GetCustomReligions());
	}

	[ElinCharaOnCreate] 
	private static void OnCharaCreated(Chara chara)
	{
		if (TryGetContent<CustomCharaContent>("Chara/" + chara.id, out var content))

private static void OnCharaCreated(Chara chara)

cs
		}
	}

	[ElinThingOnCreate] 
	private static void OnThingCreated(Thing thing)
	{
		if (TryGetContent<CustomThingContent>("Thing/" + thing.id, out var content))

private static void OnThingCreated(Thing thing)

cs
		}
	}

	[ElinActPerform] 
	private static void OnActPerformed(Act act)
	{
		if (act.HasTag("godAbility"))

private static void OnSetLang(string lang)

cs
		PackageIterator.ClearCache();
		PackageIterator.RebuildAllMappings(lang);
		SourceLocalization.SetLang(lang);
		if (ModManagerCore.generateLocalizations) 
		foreach (EMod activatedUserMod in ModManager.Instance.ActivatedUserMods) 
		{
			foreach (EMod activatedUserMod in ModManager.Instance.ActivatedUserMods) 
			if (activatedUserMod.IsSourceLocalizable && (activatedUserMod.isInPackages || Application.isEditor)) 
			{
				if (activatedUserMod.IsSourceLocalizable && (activatedUserMod.isInPackages || Application.isEditor)) 
				{ 
					activatedUserMod.UpdateSourceLocalizationFile(lang); 
				} 
				activatedUserMod.UpdateSourceLocalizationFile(lang); 
			}
			ModManagerCore.generateLocalizations = false; 
		}
		foreach (string item in LoadGodTalk())
		{
			MOD.listGodTalk.Add(new ExcelData(item));
		}
		foreach (string item2 in LoadCharaTalk()) 
		{ 
			MOD.listTalk.Add(new ExcelData(item2)); 
		} 
		foreach (string item3 in LoadCharaTone()) 
		{ 
			MOD.tones.Add(new ExcelData(item3)); 
		} 
		BookList.dict = null;
		BottleMessageList.list = null;
		Lang.excelDialog = null;
		foreach (CustomFileContent item2 in _customContent.Values.OfType<CustomFileContent>()) 
		foreach (CustomFileContent item4 in _customContent.Values.OfType<CustomFileContent>()) 
		{
			item2.OnSetLang(lang); 
			item4.OnSetLang(lang); 
		}
	}

public static bool HasContent(string contentId)

cs

	public static void AddContent(ICustomContent content)
	{
		UnityEngine.Debug.Log(_customContent.Remove(content.ContentId, out var value) ? ("#mod-content override '" + content.ContentId + "' '" + value.Owner.id + "' -> '" + content.Owner.id + "'") : ("#mod-content added '" + content.ContentId + "' from '" + content.Owner.id + "'")); 
		UnityEngine.Debug.Log(_customContent.Remove(content.ContentId, out var value) ? ("#mod-content override '" + content.ContentId + "' from '" + value.Owner.id + "' to '" + content.Owner.id + "'") : ("#mod-content added '" + content.ContentId + "' from '" + content.Owner.id + "'")); 
		_customContent[content.ContentId] = content;
	}

public static List<Thing> GenerateMerchantStock(Card owner, string stockId = nul

cs
		return list;
	}

	public static Sprite AppendSpriteSheet(string id, int resizeWidth = 0, int resizeHeight = 0, string pattern = "@") 
	public static void RegisterElinEventAttributes() 
	{
		Dictionary<string, string> dictModItems = SpriteReplacer.dictModItems; 
		if (!dictModItems.TryGetValue(id, out var value) && pattern != "") 
		{ 
			value = dictModItems.Where((KeyValuePair<string, string> kv) => kv.Key.StartsWith(pattern)).FirstOrDefault((KeyValuePair<string, string> kv) => id.StartsWith(kv.Key[pattern.Length..])).Value; 
		} 
		string spritePath = value; 
		string name = id; 
		Sprite sprite = LoadSprite(spritePath, null, name, resizeWidth, resizeHeight); 
		if (sprite == null) 
		{ 
			return null; 
		} 
		if (SpriteSheet.dict.TryGetValue(id, out var value2) && value2.texture.width == sprite.texture.width && value2.texture.height == sprite.texture.height) 
		ClassCache.modTypes.Add(typeof(ModUtil)); 
		ClassCache.modTypes.Add(typeof(CustomDramaExpansion)); 
		foreach (var item3 in ClassCache.modTypes.Except(_checkedAttributedTypes).MembersWith<ElinEventBaseAttribute>()) 
		{
			return value2; 
			MemberInfo item = item3.member; 
			ElinEventBaseAttribute[] item2 = item3.attrs; 
			foreach (ElinEventBaseAttribute elinEventBaseAttribute in item2) 
			{ 
				try
				{ 
					if (!(item is PropertyInfo property)) 
					{ 
						if (item is MethodInfo method) 
						{ 
							elinEventBaseAttribute.Register(method); 
						} 
					} 
					else
					{ 
						elinEventBaseAttribute.Register(property); 
					} 
				} 
				catch (Exception ex) 
				{ 
					LogModError("exception while registering attribute '" + elinEventBaseAttribute.GetType().Name + "' from '" + item.TryToString() + "'\n" + ex.Message, item.DeclaringType); 
					UnityEngine.Debug.LogException(ex); 
				} 
			} 
		}
		return SpriteSheet.dict[sprite.name] = sprite; 
		_checkedAttributedTypes.UnionWith(ClassCache.modTypes); 
	}

	public static List<string> LoadBookList()

public static List<string> LoadBookList()

cs
	foreach (DirectoryInfo directoryInfo in directories)
	{
		list.AddRange(Directory.GetDirectories(directoryInfo.FullName));
		UnityEngine.Debug.Log("#book list loaded " + directoryInfo.ShortPath()); 
		UnityEngine.Debug.Log("#mod-content loaded book list " + directoryInfo.ShortPath()); 
	}
	return list;
}

public static List<string> LoadTopicFiles()

cs
	foreach (FileInfo fileInfo in files)
	{
		list.AddRange(IO.LoadTextArray(fileInfo.FullName));
		UnityEngine.Debug.Log("#book topic loaded " + fileInfo.ShortPath()); 
		UnityEngine.Debug.Log("#mod-content loaded book topics " + fileInfo.ShortPath()); 
	}
	return list;
}

public static List<string> LoadExcelDialog()

cs
		foreach (FileInfo fileInfo in files)
		{
			list.Add(fileInfo.FullName);
			UnityEngine.Debug.Log("#dialog loaded " + fileInfo.ShortPath()); 
			UnityEngine.Debug.Log("#mod-content loaded dialog " + fileInfo.ShortPath()); 
		} 
		return list; 
	} 
	public static List<string> LoadCharaTalk() 
	{ 
		List<string> list = new List<string>(); 
		FileInfo[] files = PackageIterator.GetFiles("Data/chara_talk.xlsx"); 
		foreach (FileInfo fileInfo in files) 
		{ 
			list.Add(fileInfo.FullName); 
			UnityEngine.Debug.Log("#mod-content loaded chara tone " + fileInfo.ShortPath()); 
		} 
		return list; 
	} 
	public static List<string> LoadCharaTone() 
	{ 
		List<string> list = new List<string>(); 
		FileInfo[] files = PackageIterator.GetFiles("Data/chara_tone.xlsx"); 
		foreach (FileInfo fileInfo in files) 
		{ 
			list.Add(fileInfo.FullName); 
			UnityEngine.Debug.Log("#mod-content loaded chara talk " + fileInfo.ShortPath()); 
		}
		return list;
	}

public static Sprite LoadSprite(string spritePath, Vector2? pivot = null, string

cs
	}
	catch (Exception arg)
	{
		UnityEngine.Debug.LogError($"#sprite failed to load {spritePath.ShortPath()}\n{arg}"); 
		UnityEngine.Debug.LogError($"#mod-content failed to load sprite {spritePath.ShortPath()}\n{arg}"); 
		return null;
	}
	Texture2D texture2D2 = _cachedTextures[text];

public static Sprite LoadSprite(string spritePath, Vector2? pivot = null, string

cs
		return sprite;
	}

	public static Sprite AppendSpriteSheet(string id, int resizeWidth = 0, int resizeHeight = 0, string pattern = "@") 
	{ 
		Dictionary<string, string> dictModItems = SpriteReplacer.dictModItems; 
		if (!dictModItems.TryGetValue(id, out var value) && pattern != "") 
		{ 
			value = dictModItems.Where((KeyValuePair<string, string> kv) => kv.Key.StartsWith(pattern)).FirstOrDefault((KeyValuePair<string, string> kv) => id.StartsWith(kv.Key[pattern.Length..])).Value; 
		} 
		string spritePath = value; 
		string name = id; 
		Sprite sprite = LoadSprite(spritePath, null, name, resizeWidth, resizeHeight); 
		if (sprite == null) 
		{ 
			return null; 
		} 
		if (SpriteSheet.dict.TryGetValue(id, out var value2) && value2.texture.width == sprite.texture.width && value2.texture.height == sprite.texture.height) 
		{ 
			return value2; 
		} 
		return SpriteSheet.dict[sprite.name] = sprite; 
	} 
	public static List<string> LoadGodTalk()
	{
		List<string> list = new List<string>();

public static List<string> LoadGodTalk()

cs
		foreach (FileInfo fileInfo in files)
		{
			list.Add(fileInfo.FullName);
			UnityEngine.Debug.Log("#god-talk loaded " + fileInfo.ShortPath()); 
			UnityEngine.Debug.Log("#mod-content loaded god talk " + fileInfo.ShortPath()); 
		}
		return list;
	}

	public static Dictionary<string, CustomGunEffectData> LoadGunEffects() 
	{ 
		Dictionary<string, CustomGunEffectData> dictionary = new Dictionary<string, CustomGunEffectData>(); 
		(FileInfo, EMod)[] filesEx = PackageIterator.GetFilesEx("Data/EffectSetting.guns.json"); 
		for (int i = 0; i < filesEx.Length; i++) 
		{ 
			(FileInfo, EMod) tuple = filesEx[i]; 
			FileInfo item = tuple.Item1; 
			EMod item2 = tuple.Item2; 
			CustomGunEffectSetting customGunEffectSetting = CustomGunEffectSetting.CreateFromFile(item, item2 as ModPackage); 
			_customContent[customGunEffectSetting.ContentId] = customGunEffectSetting; 
		} 
		foreach (CustomGunEffectSetting item3 in _customContent.Values.OfType<CustomGunEffectSetting>()) 
		{ 
			item3.Load(); 
			dictionary.Merge(item3.items); 
			UnityEngine.Debug.Log($"#mod-content loaded {item3}"); 
		} 
		return dictionary; 
	} 
	public static void ImportAllGunEffectSettings() 
	{ 
		foreach (KeyValuePair<string, CustomGunEffectData> item in LoadGunEffects()) 
		{ 
			item.Deconstruct(out var key, out var value); 
			string key2 = key; 
			GameSetting.EffectData value2 = value.CreateEffectData(); 
			EClass.setting.effect.guns[key2] = value2; 
		} 
	} 
	public static string ExportAllGunEffectSettings() 
	{ 
		UD_String_EffectData guns = EClass.setting.effect.guns; 
		string text = CorePath.rootExe + "/guns.json"; 
		Dictionary<string, CustomGunEffectData> dictionary = new Dictionary<string, CustomGunEffectData>(); 
		foreach (string key in guns.Keys) 
		{ 
			CustomGunEffectData value = CustomGunEffectData.CreateFromId(key); 
			dictionary[key] = value; 
		} 
		File.WriteAllText(text, JsonConvert.SerializeObject(dictionary, Formatting.Indented, GameIOContext.Settings)); 
		return $"dumped {dictionary.Count} guns data to {text}"; 
	} 
	private static Effect LoadEffect(string id) 
	{ 
		if (id.IsEmpty()) 
		{ 
			return null; 
		} 
		if (!_rodTemplate) 
		{ 
			return null; 
		} 
		string effectId = id.Split('/')[^1]; 
		Sprite sprite = LoadSprite(effectId); 
		if (!sprite) 
		{ 
			return null; 
		} 
		Effect effect = UnityEngine.Object.Instantiate(_rodTemplate); 
		effect.name = effectId; 
		effect.sprites = Slice().ToArray(); 
		UnityEngine.Object.DontDestroyOnLoad(effect); 
		UnityEngine.Debug.Log("#mod-content loaded custom effect '" + effectId + "'"); 
		return effect; 
		IEnumerable<Sprite> Slice() 
		{ 
			int height = (int)sprite.rect.height; 
			float frames = sprite.rect.width / (float)height; 
			if (frames != 0f) 
			{ 
				int i = 0; 
				while ((float)i < frames) 
				{ 
					Sprite sprite2 = Sprite.Create(rect: new Rect(i * height, 0f, height, height), texture: sprite.texture, pivot: new Vector2(0.5f, 0.5f * (128f / (float)height)), pixelsPerUnit: 100f, extrude: 0u, meshType: SpriteMeshType.FullRect); 
					sprite2.name = $"{effectId}{i:D4}"; 
					yield return sprite2; 
					int num = i + 1; 
					i = num; 
				} 
			} 
		} 
	} 
	private static void LoadEffectTemplate() 
	{ 
		_rodTemplate = Resources.Load<Effect>("Media/Effect/General/rod"); 
		if (!_rodTemplate) 
		{ 
			UnityEngine.Debug.LogWarning("#mod-content cannot initialize rod effect template"); 
		} 
	} 
	public static SerializableSoundData GetSoundMeta(string soundPath)
	{
		string path = Path.ChangeExtension(soundPath, ".json");

public static void ImportModSourceSheets(string modId)

cs
			list.Add(sourceSheet.FullName);
		}
		sourceImporter.ImportFilesCached(list);
		if (ModManagerCore.enableSheetCaching) 
		{ 
			SourceCache.FinalizeCache(); 
		} 
		SourceCache.InvalidateCacheBlobs(); 
		SourceCache.ClearDetail(); 
		UnityEngine.Debug.Log("#source finished importing workbooks from " + modId);
	}
	catch (Exception exception)

public static void ImportAllModSourceSheets()

cs
			}
		}
		sourceImporter.ImportFilesCached(list);
		if (ModManagerCore.enableSheetCaching) 
		{ 
			SourceCache.FinalizeCache(); 
		} 
		SourceCache.InvalidateCacheBlobs(); 
		SourceCache.ClearDetail(); 
	}
	catch (Exception message) 
	catch (Exception exception) 
	{
		UnityEngine.Debug.LogError(message); 
		UnityEngine.Debug.LogException(exception); 
	}
	UnityEngine.Debug.Log("#source finished importing workbooks");
}

public static string ExportSourceDataCsv(string sourceData, string delimiter = "

cs
		return "";
	}
	IReadOnlyDictionary<string, string> typeMapping = sourceData2.GetTypeMapping();
	IReadOnlyDictionary<string, int> rowMapping = sourceData2.GetRowMapping(); 
	StringBuilder stringBuilder = new StringBuilder(); 
	stringBuilder.AppendLine(string.Join(delimiter, rowMapping.Keys)); 
	stringBuilder.AppendLine(string.Join(delimiter, rowMapping.Keys.Select((string k) => typeMapping[k]))); 
	(string, string)[] array = (from kv in rowMapping 
	(string, string)[] array = (from kv in sourceData2.GetRowMapping() 
		orderby kv.Value
		select (Key: kv.Key, typeMapping[kv.Key])).ToArray(); 
		select (column: kv.Key, type: typeMapping[kv.Key])).ToArray(); 
	StringBuilder stringBuilder = new StringBuilder(); 
	stringBuilder.AppendLine(string.Join(delimiter, array.Select(((string column, string type) p) => p.column))); 
	stringBuilder.AppendLine(string.Join(delimiter, array.Select(((string column, string type) p) => p.type))); 
	SourceData.BaseRow[] array2 = sourceData2.ExportRows();
	foreach (SourceData.BaseRow baseRow in array2)
	{

orderby kv.Value

cs
					enumerable = obj as IEnumerable;
					if (enumerable != null)
					{
						goto IL_0221; 
						goto IL_024a; 
					}
				}
				else if (item2 == "element_id")

orderby kv.Value

cs
			if (!(item2 == "elements"))
			{
				enumerable = (IEnumerable)obj;
				goto IL_0221; 
				goto IL_024a; 
			}
			List<string> list2 = new List<string>();
			for (int l = 0; l < array4.Length - 1; l += 2) 
			for (int k = 0; k < array4.Length - 1; k += 2) 
			{
				string alias = EClass.sources.elements.map[array4[l]].alias; 
				string text = array4[l + 1].ToString(); 
				string alias = EClass.sources.elements.map[array4[k]].alias; 
				string text = array4[k + 1].ToString(); 
				list2.Add(alias + "/" + text);
			}
			string item4 = string.Join(',', list2);
			list.Add(item4);
			continue;
			IL_0221: 
			IL_024a: 
			string item5 = string.Join(',', enumerable.OfType<object>());
			list.Add(item5);
		}
		stringBuilder.AppendLine(string.Join(delimiter, list.Select((string f) => "\"" + f.RemoveNewline() + "\""))); 
		IEnumerable<string> values = list.Select(delegate(string f) 
		{ 
			string text2 = f.Replace("\r", "").Replace("\n", "\\n").Replace("\"", "\"\""); 
			return (!text2.IsEmpty()) ? ("\"" + text2 + "\"") : ""; 
		}); 
		stringBuilder.AppendLine(string.Join(delimiter, values)); 
	}
	return stringBuilder.ToString();
}

public static void ExportAllSourceDataJson(string dir)

cs
			File.WriteAllText(Path.Combine(dir, key + ".json"), ExportSourceDataJson(key));
		}
	}
	public static void AddContextMenuEntry(Action onClick, string menuEntry, string displayName = "") 
	{ 
		if (onClick == null || menuEntry.IsEmpty()) 
		{ 
			return; 
		} 
		string[] array = menuEntry.Split('/', StringSplitOptions.RemoveEmptyEntries); 
		List<ContextMenuProxy> children = contextMenuProxies; 
		for (int i = 0; i < array.Length; i++) 
		{ 
			bool flag = i < array.Length - 1; 
			string part = array[i]; 
			ContextMenuProxy contextMenuProxy = children.Find((ContextMenuProxy p) => p.MenuEntry == part); 
			if (contextMenuProxy == null) 
			{ 
				contextMenuProxy = new ContextMenuProxy(part, flag ? part : displayName) 
				{ 
					onClick = ((i == array.Length - 1 && flag) ? null : new Action(SafeInvoke)), 
					isMenu = flag 
				}; 
				children.Add(contextMenuProxy); 
			} 
			else if (contextMenuProxy.isMenu != flag) 
			{ 
				UnityEngine.Debug.LogWarning("#mod-content attempt to add context menu entry with same name but different types\n" + part + " -> " + (contextMenuProxy.isMenu ? "submenu" : "button")); 
				return; 
			} 
			children = contextMenuProxy.children; 
		} 
		UnityEngine.Debug.Log("#mod-content added context menu entry '" + menuEntry + "' from '" + onClick.Method.TryToString() + "'"); 
		void SafeInvoke() 
		{ 
			try
			{ 
				onClick(); 
			} 
			catch (Exception exception) 
			{ 
				UnityEngine.Debug.LogException(exception); 
			} 
		} 
	} 
	public static void RemoveContextMenuEntry(string entry) 
	{ 
		string[] array = entry.Split('/', StringSplitOptions.RemoveEmptyEntries); 
		if (array.Length == 0) 
		{ 
			return; 
		} 
		List<ContextMenuProxy> children = contextMenuProxies; 
		ContextMenuProxy contextMenuProxy = null; 
		List<ContextMenuProxy> list = null; 
		string[] array2 = array; 
		foreach (string part in array2) 
		{ 
			contextMenuProxy = children.Find((ContextMenuProxy p) => p.MenuEntry == part); 
			if (contextMenuProxy == null) 
			{ 
				return; 
			} 
			list = children; 
			children = contextMenuProxy.children; 
		} 
		list?.Remove(contextMenuProxy); 
	} 
}

Point โ€‹

public override string ToString()

cs
		return "(" + x + " / " + z + ")";
	}

	public static Point operator +(Point lhs, Point rhs) 
	{ 
		return new Point(lhs.x + rhs.x, lhs.z + rhs.z); 
	} 
	public void Set(Vector3 v)
	{
		v.x -= 0.64f;

Props โ€‹

public void Remove(Card t)

cs
	weight -= t.Num;
	all.Remove(t);
	things.Remove(t.Thing);
	cardMap[t.id].Remove(t); 
	cardMap.TryGetValue(t.id)?.Remove(t); 
	if (t.sourceCard.origin != null)
	{
		cardMap[t.sourceCard.origin.id].Remove(t); 
		cardMap.TryGetValue(t.sourceCard.origin.id)?.Remove(t); 
	}
	categoryMap[t.category.id].Remove(t);
	if (!t.Thing.source.workTag.IsEmpty())

QuestDeliver โ€‹

public class QuestDeliver : QuestDestZone

cs

	public virtual bool ConsumeGoods => false;

	public SourceThing.Row sourceThing => EClass.sources.things.map[idThing.IsEmpty("generator_snowman")]; 
	public SourceThing.Row sourceThing => EClass.sources.things.map.TryGetValue(idThing.IsEmpty("generator_snowman"), "generator_snowman"); 

	public override string NameDeliver => sourceThing.GetName();

public virtual bool IsDestThing(Thing t)

cs
		if (t.c_altName.IsEmpty())
		{
			string name = sourceThing.GetName();
			if (t.source.GetName() == name || t.GetName(NameStyle.Simple, 1) == name) 
			try
			{
				return true; 
				if (t.source.GetName() == name || t.GetName(NameStyle.Simple, 1) == name) 
				{ 
					return true; 
				} 
			} 
			catch
			{ 
				return false; 
			}
		}
	}

RecipeCard โ€‹

public override void Build(TaskBuild task)

cs
	}
	else
	{
		card = ThingGen.Create(idCard, -1, Mathf.Max(EClass._zone.DangerLv, EClass.pc.LV)); 
		CardBlueprint.Set(new CardBlueprint
		{ 
			rarity = Rarity.Normal, 
			isCraft = true
		}); 
		card = ThingGen.Create(idCard); 
		if (!card.IsUnique)
		{
			card.ChangeMaterial(GetMainMaterial());

RecipeManager โ€‹

using System;

cs
using System; 
using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

public static void Create(RenderRow row, string type, string suffix = "")

cs
		list.Add(recipeSource);
		dict[recipeSource.id] = recipeSource;
		_ = row.components;
		Recipe.Create(recipeSource).BuildIngredientList(); 
		try
		{ 
			Recipe.Create(recipeSource).BuildIngredientList(); 
		} 
		catch (Exception ex) 
		{ 
			list.Remove(recipeSource); 
			dict.Remove(recipeSource.id); 
			Debug.LogWarning($"Failed to create recipe '{row.RecipeID}' '{type}{suffix}'\n{ex}"); 
		} 
	}

	public static RecipeSource Get(string id)

Religion โ€‹

public virtual bool TryGetGift()

cs
	public virtual void OnReforge(Thing t)
	{
		t.c_idDeity = id;
		if (IsIgnoreReforge(t)) 
		{ 
			return; 
		} 
		foreach (Element value in t.elements.dict.Values)
		{
			if (IsFaithElement(value)) 
			int num = value.id; 
			if ((uint)(num - 64) > 3u && IsFaithElement(value)) 
			{
				value.vExp = -1;
			}
		}
	}

	public virtual bool IsIgnoreReforge(Thing t) 
	{ 
		return false; 
	} 
	public virtual bool IsValidArtifact(string id)
	{
		return false;

ReligionMachine โ€‹

public class ReligionMachine : Religion

cs
{
	public override string id => "machine";

	public override void OnReforge(Thing t) 
	public override bool IsIgnoreReforge(Thing t) 
	{
		t.c_idDeity = id; 
		if (t.id == "gun_mani") 
		{ 
			return; 
		} 
		foreach (Element value in t.elements.dict.Values) 
		{ 
			if (IsFaithElement(value)) 
			{ 
				value.vExp = -1; 
			} 
		} 
		return t.id == "gun_mani"; 
	}

	public override bool IsValidArtifact(string id)

ReligionWind โ€‹

public class ReligionWind : Religion

cs
{
	public override string id => "wind";

	public override void OnReforge(Thing t) 
	public override bool IsIgnoreReforge(Thing t) 
	{
		t.c_idDeity = id; 
		if (t.id == "windbow") 
		{ 
			return; 
		} 
		foreach (Element value in t.elements.dict.Values) 
		{ 
			if (IsFaithElement(value)) 
			{ 
				value.vExp = -1; 
			} 
		} 
		return t.id == "windbow"; 
	}

	public override bool IsValidArtifact(string id)

SourceManager โ€‹

using System.Collections.Generic;

cs
using System.Collections.Generic;
using System.Linq; 
using System.Reflection;
using UnityEngine;

public void Init()

cs
		keyItems.Init();
		ACT.Init();
		TimeTable.Init();
		List<SourceElement.Row> listAttackElements = Element.ListAttackElements; 
		listAttackElements.Clear(); 
		for (int j = 910; j < 927; j++) 
		{ 
			listAttackElements.Add(EMono.sources.elements.map[j]); 
		} 
		Element.ListAttackElements.Clear(); 
		Element.ListAttackElements.AddRange(EMono.sources.elements.rows.Where((SourceElement.Row r) => r.categorySub == "eleAttack")); 
		BaseModManager.PublishEvent("elin.source.imported");
	}

SourceMaterial โ€‹

using System;

cs
using System;
using System.Collections.Generic;
using System.Linq; 
using UnityEngine;

public class SourceMaterial : SourceDataInt<SourceMaterial.Row>

public override void OnImportData(SourceData data)

cs
	public void Init()
	{
		MatColors matColors = Core.Instance.Colors.matColors.TryGetValue(alias);
		HashSet<string> hashSet = new HashSet<string>(tag) { alias }; 
		if (matColors == null)
		{
			matColors = new MatColors();
			string[] array = tag; 
			for (int i = 0; i < array.Length; i++) 
			foreach (string item in hashSet) 
			{
				var (text, s, _) = CustomSourceContent.GetParams(array[i]); 
				var (text, s, _) = CustomSourceContent.GetParams(item); 
				switch (text)
				{
				case "addCol_Main":

public void Init()

cs
			}
			matColor = matColors.main;
			altColor = matColors.alt;
			tag = hashSet.ToArray(); 
			SetTiles();
		}

TCUI โ€‹

public override void OnDraw(ref Vector3 pos)

cs
			lastPos = _pos;
			Vector3 position = Camera.main.WorldToScreenPoint(_pos);
			position.z = 0f;
			position += FixPos * EMono.screen.Zoom; 
			Vector3 vector = FixPos; 
			if (render.hasActor && !render.actor.isPCC && (bool)render.actor && (bool)render.actor.sr.sprite) 
			{ 
				float num = 128f / render.actor.sr.sprite.rect.height; 
				int pivotY = render.owner.Pref.pivotY; 
				vector = FixPos + new Vector3(0f, num * ((float)pivotY - 48f), 0f); 
			} 
			position += vector * EMono.screen.Zoom; 
			_rect.position = position;
		}
	});

TraitAdventurerCustom โ€‹

public class TraitAdventurerCustom : TraitAdventurerBacker

cs
public class TraitAdventurerCustom : TraitAdventurerBacker
{
	public override Adv_Type AdvType => Adv_Type.Adv_Custom;
	public override ShopType ShopType => ShopType.CustomContent; 
}

Zone_Tent โ€‹

public class Zone_Tent : Zone

cs

	public override bool IsUnderwater => elements.Has(3606);

	public override int DangerLv => 1; 
	public override ZoneTransition.EnterState RegionEnterState => ZoneTransition.EnterState.Bottom;

	public override void OnBeforeDeactivate()