Skip to content

EA 23.187 Stable

August 24, 2025

21 files modified. 4 new files created.

Important Changes

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

Map (1)

cs
public bool TrySpawnSurvivalItem(Point point) 

AIWork

using System;

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

public class AIWork : AIAct
{

public bool SetDestination()

cs
	}
	if (!sourceWork.destTrait.IsEmpty())
	{
		if (Application.isEditor && Type.GetType("Trait" + sourceWork.destTrait + ", Elin") == null) 
		{ 
			Debug.Log(sourceWork.destTrait + "/" + sourceWork.alias); 
		} 
		if (destArea != null)
		{
			destThing = EClass._map.FindThing(Type.GetType("Trait" + sourceWork.destTrait + ", Elin"), destArea);

AI_Idle

public override IEnumerable<Status> Run()

cs
	{
		owner.AddCondition<ConSleep>(1000 + EClass.rnd(1000), force: true);
	}
	if (EClass.rnd(100) == 0 && (owner.HasHobbyOrWork("Pet") || owner.HasHobbyOrWork("Fluffy"))) 
	{ 
		yield return Do(new AI_Mofu()); 
	} 
	if (EClass.rnd((owner.host != null && owner.GetInt(106) != 0) ? 1000 : 40) == 0 && owner.IsHuman)
	{
		DoSomethingToNearChara((Chara c) => (!c.IsPCParty || EClass.rnd(5) == 0) && c.IsMofuable && !owner.IsHostile(c) && !c.IsInCombat, delegate(Chara c)

+AI_Mofu

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

public class AI_Mofu : AIWork
{
	public Chara mofu;

	public override int MaxRestart => 100;

	public Chara GetMofu()
	{
		return EClass._map.charas.Where((Chara c) => c.IsMofuable).RandomItem();
	}

	public override IEnumerable<Status> Run()
	{
		yield return DoWait(3 + EClass.rnd(10));
		if (mofu == null)
		{
			mofu = GetMofu();
		}
		if (mofu == null || !mofu.ExistsOnMap)
		{
			yield return Success();
		}
		if (owner.Dist(mofu) > 1)
		{
			yield return DoGoto(mofu);
		}
		if (mofu == null || !mofu.ExistsOnMap)
		{
			yield return Success();
		}
		owner.Cuddle(mofu);
		if (EClass.rnd(222) != 0)
		{
			yield return Restart();
		}
	}
}

ActPlan

void func()

cs
					}, null, CursorSystem.Hand, 1, isHostileAct: false, localAct: false);
				}
			}
			if (input == ActInput.AllAction && pos.IsSky) 
			if (input == ActInput.AllAction && pos.IsSky && !EClass.game.IsSurvival) 
			{
				TrySetAct("actSkyJump", delegate
				{

Chara

public override void Tick()

cs
			EClass.player.returnInfo.turns--;
			if (EClass.player.returnInfo.turns <= 0)
			{
				if (EClass.game.Prologue.type == GameType.Survival && EClass._zone is Zone_StartSiteSky) 
				if (EClass.game.IsSurvival && EClass._zone is Zone_StartSiteSky) 
				{
					Msg.SayNothingHappen();
				}

public Goal GetGoalHobby()

cs
		return new GoalHobby();
	}

	public bool HasHobbyOrWork(string alias) 
	{ 
		int num = EClass.sources.hobbies.alias.TryGetValue(alias)?.id ?? 0; 
		listHobby.Clear(); 
		if (_works == null || _hobbies == null) 
		{ 
			RerollHobby(); 
		} 
		foreach (int work in _works) 
		{ 
			if (work == num) 
			{ 
				return true; 
			} 
		} 
		foreach (int hobby in _hobbies) 
		{ 
			if (hobby == num) 
			{ 
				return true; 
			} 
		} 
		return false; 
	} 
	public void SetAIIdle()
	{
	}

DramaManager

public bool CheckIF(string IF)

cs
	switch (array[0])
	{
	case "survival":
		return EMono.game.Prologue.type == GameType.Survival; 
		return EMono.game.IsSurvival; 
	case "fromBook":
		return LayerDrama.fromBook;
	case "!fromBook":

ElementContainer

public List<Element> ListBestSkills()

cs

	public List<Element> ListGeneFeats()
	{
		return ListElements((Element a) => a.Value > 0 && a.source.category == "feat" && a.source.cost.Length != 0 && a.source.cost[0] > 0 && a.source.geneSlot >= 0); 
		return ListElements((Element a) => a.ValueWithoutLink > 0 && a.source.category == "feat" && a.source.cost.Length != 0 && a.source.cost[0] > 0 && a.source.geneSlot >= 0); 
	}

	public List<Element> ListLearnable(Chara c)

FactionBranch

public void UpdateReqruits(bool clear = false)

cs
				AddRecruit(chara);
			}
		}
		if (EClass.game.IsSurvival) 
		{ 
			EClass.game.survival.OnUpdateRecruit(this); 
		} 
	}

	public void AddRecruit(Chara c)

Game

private void _OnDeserialized(StreamingContext context)

cs

	public bool altAbility => EClass.core.config.game.altAbility;

	public bool IsSurvival => Prologue.type == GameType.Survival; 
	public Zone StartZone => spatials.Find(EClass.game.Prologue.idStartZone);

	public new static void Wait(float a, Point p)

LayerQuestBoard

public override void OnSwitchContent(Window window)

cs
		RefreshQuest();
		break;
	case 1:
		if (ELayer.debug.enable) 
		{ 
			ELayer.Branch.UpdateReqruits(clear: true); 
		} 
		RefreshHire();
		break;
	case 2:

Map

public void Reload()

cs
	string id = Game.id;
	EClass.game.Save();
	EClass.scene.Init(Scene.Mode.None);
	Game.Load(id, EClass.game.isCloud); 
	Game.Load(id, cloud: false); 
	RevealAll();
	TweenUtil.Tween(0.1f, null, delegate
	{

public void MineObj(Point point, Task task = null, Chara c = null)

cs
			{
				MineBlock(point, recoverBlock: false, c, mineObj: false);
			}
			if (EClass.game.Prologue.type == GameType.Survival && EClass._zone is Zone_StartSiteSky && !EClass.scene.actionMode.IsBuildMode) 
			if (EClass.game.IsSurvival && EClass._zone is Zone_StartSiteSky && !EClass.scene.actionMode.IsBuildMode) 
			{
				if (TrySpawnSurvivalItem(point)) 
				if (EClass.game.survival.OnMineWreck(point)) 
				{
					Rand.SetSeed();
					return;

public void MineObjSound(Point point)

cs
		point.PlaySound(point.cell.matObj.GetSoundDead(point.cell.sourceObj));
	}

	public bool TrySpawnSurvivalItem(Point point) 
	{ 
		SourceObj.Row sourceObj = point.cell.sourceObj; 
		int searchWreck = EClass.game.survival.flags.searchWreck; 
		string[] array = new string[6] { "log", "rock", "branch", "bone", "grass", "vine" }; 
		int chanceChange = 25; 
		int num = searchWreck / 50 + 3; 
		switch (sourceObj.alias) 
		{ 
		case "nest_bird": 
			chanceChange = 100; 
			return Pop(ThingGen.Create((EClass.rnd(10) == 0) ? "egg_fertilized" : "_egg").TryMakeRandomItem(num)); 
		case "wreck_wood": 
			array = new string[5] { "log", "log", "branch", "grass", "vine" }; 
			break; 
		case "wreck_junk": 
			chanceChange = 50; 
			return Pop(ThingGen.CreateFromFilter("shop_junk", num)); 
		case "wreck_stone": 
			array = new string[4] { "rock", "rock", "stone", "bone" }; 
			break; 
		case "wreck_scrap": 
			chanceChange = 75; 
			array = new string[1] { "scrap" }; 
			break; 
		case "wreck_cloth": 
			chanceChange = 75; 
			array = new string[1] { "fiber" }; 
			break; 
		case "wreck_precious": 
			chanceChange = 100; 
			return Pop(ThingGen.CreateFromFilter("shop_magic", num)); 
		default: 
			return false; 
		} 
		if (EClass.rnd(3) == 0 && EClass.game.survival.flags.spawnedFloor < 4) 
		{ 
			EClass.game.survival.flags.spawnedFloor++; 
			return Pop(ThingGen.CreateFloor(40, 45).SetNum(3)); 
		} 
		if (EClass.rnd(20) == 0) 
		{ 
			return Pop(TraitSeed.MakeRandomSeed()); 
		} 
		if (EClass.rnd(12) == 0) 
		{ 
			return Pop(ThingGen.Create("money2")); 
		} 
		if (EClass.rnd(12) == 0) 
		{ 
			Point pos = point.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: false, ignoreCenter: true) ?? point; 
			if (searchWreck < 50 || EClass.rnd(3) != 0) 
			{ 
				EClass._zone.SpawnMob(pos, SpawnSetting.HomeWild(num)); 
			} 
			else
			{ 
				EClass._zone.SpawnMob(pos, SpawnSetting.HomeEnemy(Mathf.Max(num - 5, 1))); 
			} 
		} 
		return Pop(ThingGen.Create(array.RandomItem()).SetNum(1 + EClass.rnd(3))); 
		bool Next() 
		{ 
			EClass.game.survival.flags.searchWreck++; 
			Point pos2 = point.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: false, ignoreCenter: true) ?? point; 
			if (EClass.game.survival.flags.searchWreck == 20) 
			{ 
				EffectMeteor.Create(pos2, 0, 1, delegate
				{ 
					EClass._zone.AddCard(ThingGen.CreateRecipe("container_shipping"), pos2); 
				}); 
			} 
			NextObj(); 
			return true; 
		} 
		void NextObj() 
		{ 
			if (EClass.rnd(100) < chanceChange) 
			{ 
				string[] source = new string[11] 
				{ 
					"nest_bird", "wreck_wood", "wreck_wood", "wreck_wood", "wreck_wood", "wreck_stone", "wreck_stone", "wreck_scrap", "wreck_junk", "wreck_cloth", 
					"wreck_precious"
				}; 
				SetObj(point.x, point.z, EClass.sources.objs.alias[source.RandomItem()].id); 
			} 
		} 
		bool Pop(Thing t) 
		{ 
			TrySmoothPick(point, t, EClass.pc); 
			Next(); 
			return true; 
		} 
	} 
	public PlantData TryGetPlant(Point p)
	{
		return plants.TryGetValue(p.index);

SurvivalManager

using System;

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

public class SurvivalManager : EClass
{

public int searchWreck

cs
			}
		}

		public int dateNextRaid 
		{ 
			get 
			{ 
				return ints[6]; 
			} 
			set 
			{ 
				ints[6] = value; 
			} 
		} 
		public int raidRound 
		{ 
			get 
			{ 
				return ints[7]; 
			} 
			set 
			{ 
				ints[7] = value; 
			} 
		} 
		public bool raid 
		{ 
			get 
			{ 
				return bits[0]; 
			} 
			set 
			{ 
				bits[0] = value; 
			} 
		} 
		[OnSerializing]
		private void _OnSerializing(StreamingContext context)
		{

private void _OnDeserialized(StreamingContext context)

cs

	[JsonProperty]
	public Flags flags = new Flags();
	public bool IsInRaid => GetRaidEvent() != null; 
	public ZoneEventRaid GetRaidEvent() 
	{ 
		return EClass._zone.events.GetEvent<ZoneEventRaid>(); 
	} 
	public void Meteor(Point pos, Action action) 
	{ 
		EffectMeteor.Create(pos, 0, 1, delegate
		{ 
			action(); 
		}); 
	} 
	public void OnExpandFloor(Point pos) 
	{ 
		int i = 0; 
		bool done = false; 
		EClass._map.ForeachCell(delegate(Cell c) 
		{ 
			if (!c.sourceFloor.tileType.IsSkipFloor) 
			{ 
				i++; 
			} 
		}); 
		Check(9, delegate
		{ 
			EClass._zone.ClaimZone(debug: false, pos); 
		}); 
		Check(15, delegate
		{ 
			EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("fiama"), pos.x, pos.z).Chara); 
		}); 
		Check(25, delegate
		{ 
			EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("nino"), pos.x, pos.z).Chara); 
		}); 
		Check(40, delegate
		{ 
			EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("loytel"), pos.x, pos.z).Chara); 
		}); 
		Check(50, delegate
		{ 
			EClass._zone.AddCard(ThingGen.Create("core_defense"), pos).Install(); 
		}); 
		void Check(int a, Action action) 
		{ 
			if (!done && flags.floors < a && i >= a) 
			{ 
				Meteor(pos, action); 
				EClass.game.survival.flags.floors = a; 
				done = true; 
			} 
		} 
	} 
	public bool OnMineWreck(Point point) 
	{ 
		if (EClass._zone.events.GetEvent<ZoneEventSurvival>() == null) 
		{ 
			EClass._zone.events.Add(new ZoneEventSurvival()); 
		} 
		SourceObj.Row sourceObj = point.cell.sourceObj; 
		int searchWreck = EClass.game.survival.flags.searchWreck; 
		string[] array = new string[6] { "log", "rock", "branch", "bone", "grass", "vine" }; 
		int chanceChange = 25; 
		int num = searchWreck / 50 + 3; 
		if (searchWreck == 0) 
		{ 
			Pop(ThingGen.Create("log").SetNum(6)); 
			Pop(ThingGen.Create("rock").SetNum(4)); 
		} 
		switch (sourceObj.alias) 
		{ 
		case "nest_bird": 
			chanceChange = 100; 
			return Pop(ThingGen.Create((EClass.rnd(10) == 0) ? "egg_fertilized" : "_egg").TryMakeRandomItem(num)); 
		case "wreck_wood": 
			array = new string[6] { "log", "log", "branch", "grass", "vine", "resin" }; 
			break; 
		case "wreck_junk": 
			chanceChange = 50; 
			return Pop(ThingGen.CreateFromFilter("shop_junk", num)); 
		case "wreck_stone": 
			chanceChange = 30; 
			array = new string[4] { "rock", "rock", "stone", "bone" }; 
			break; 
		case "wreck_scrap": 
			chanceChange = 75; 
			array = new string[1] { "scrap" }; 
			break; 
		case "wreck_cloth": 
			chanceChange = 75; 
			array = new string[1] { "fiber" }; 
			break; 
		case "wreck_precious": 
			chanceChange = 100; 
			return Pop(ThingGen.CreateFromFilter("shop_magic", num)); 
		default: 
			return false; 
		} 
		if (EClass.rnd(3) == 0 && EClass.game.survival.flags.spawnedFloor < 4) 
		{ 
			EClass.game.survival.flags.spawnedFloor++; 
			return Pop(ThingGen.CreateFloor(40, 45).SetNum(3)); 
		} 
		if (EClass.rnd(20) == 0) 
		{ 
			return Pop(TraitSeed.MakeRandomSeed()); 
		} 
		if (EClass.rnd(12) == 0) 
		{ 
			return Pop(ThingGen.Create("money2")); 
		} 
		if (EClass.rnd(12) == 0) 
		{ 
			Point pos = point.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: false, ignoreCenter: true) ?? point; 
			if (searchWreck < 50 || EClass.rnd(3) != 0) 
			{ 
				EClass._zone.SpawnMob(pos, SpawnSetting.HomeWild(num)); 
			} 
			else
			{ 
				EClass._zone.SpawnMob(pos, SpawnSetting.HomeEnemy(Mathf.Max(num - 5, 1))); 
			} 
		} 
		return Pop(ThingGen.Create(array.RandomItem()).SetNum(1 + EClass.rnd(3))); 
		bool Next() 
		{ 
			EClass.game.survival.flags.searchWreck++; 
			Point pos2 = point.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: false, ignoreCenter: true) ?? point; 
			if (EClass.game.survival.flags.searchWreck == 20) 
			{ 
				Meteor(pos2, delegate
				{ 
					EClass._zone.AddCard(ThingGen.CreateRecipe("container_shipping"), pos2); 
				}); 
			} 
			NextObj(); 
			return true; 
		} 
		void NextObj() 
		{ 
			if (EClass.rnd(100) < chanceChange) 
			{ 
				string[] source = new string[11] 
				{ 
					"nest_bird", "wreck_wood", "wreck_wood", "wreck_wood", "wreck_wood", "wreck_stone", "wreck_stone", "wreck_scrap", "wreck_junk", "wreck_cloth", 
					"wreck_precious"
				}; 
				EClass._map.SetObj(point.x, point.z, EClass.sources.objs.alias[source.RandomItem()].id); 
			} 
		} 
		bool Pop(Thing t) 
		{ 
			EClass._map.TrySmoothPick(point, t, EClass.pc); 
			Next(); 
			return true; 
		} 
	} 
	public List<SourceChara.Row> ListUnrecruitedUniques() 
	{ 
		return EClass.sources.charas.rows.Where(delegate(SourceChara.Row r) 
		{ 
			if (r.quality != 4 || r.race == "god" || r.size.Length != 0) 
			{ 
				return false; 
			} 
			switch (r.id) 
			{ 
			case "fiama": 
			case "loytel": 
			case "nino": 
			case "big_daddy": 
			case "littleOne": 
				return false; 
			default: 
				return EClass.game.cards.globalCharas.Find(r.id) == null; 
			} 
		}).ToList(); 
	} 
	public void OnUpdateRecruit(FactionBranch branch) 
	{ 
		List<SourceChara.Row> list = ListUnrecruitedUniques(); 
		for (int i = 0; i < (EClass.debug.enable ? 10 : 2); i++) 
		{ 
			SourceChara.Row row = list.RandomItem(); 
			if (row != null) 
			{ 
				Chara chara = CharaGen.Create(row.id); 
				chara.RemoveEditorTag(EditorTag.AINoMove); 
				branch.AddRecruit(chara); 
				list.Remove(row); 
			} 
		} 
	} 
	public void StartRaid() 
	{ 
		SE.Play("warhorn"); 
		Msg.Say("warhorn"); 
		flags.raid = true; 
		Point pos = EClass.pc.pos.GetNearestPoint(allowBlock: false, allowChara: false, allowInstalled: false, ignoreCenter: true) ?? EClass.pc.pos; 
		Meteor(pos, delegate
		{ 
			EClass._zone.AddCard(ThingGen.Create("teleporter_demon"), pos).Install(); 
		}); 
		flags.dateNextRaid = EClass.world.date.GetRaw(72); 
	} 
}

TCOrbitChara

public void RefreshAll()

cs
			{
				flag = true;
			}
			else if (owner.IsUnique) 
			else if (owner.IsUnique && !EMono.game.IsSurvival) 
			{
				string id = owner.id;
				if (!(id == "ashland"))

TaskBuild

public override void OnProgressComplete()

cs
				}
			});
		}
		if (EClass.game.Prologue.type != GameType.Survival || !(EClass._zone is Zone_StartSiteSky)) 
		if (EClass.game.IsSurvival && EClass._zone is Zone_StartSiteSky) 
		{
			return; 
		} 
		int i = 0; 
		EClass._map.ForeachCell(delegate(Cell c) 
		{ 
			if (!c.sourceFloor.tileType.IsSkipFloor) 
			{ 
				i++; 
			} 
		}); 
		if (EClass.game.survival.flags.floors < 9 && i >= 9) 
		{ 
			EffectMeteor.Create(pos, 0, 1, delegate
			{ 
				EClass._zone.ClaimZone(debug: false, pos); 
			}); 
			EClass.game.survival.flags.floors = 9; 
		} 
		else if (EClass.game.survival.flags.floors < 15 && i >= 15) 
		{ 
			EffectMeteor.Create(pos, 0, 1, delegate
			{ 
				EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("fiama"), pos.x, pos.z).Chara); 
			}); 
			EClass.game.survival.flags.floors = 15; 
		} 
		else if (EClass.game.survival.flags.floors < 25 && i >= 25) 
		{ 
			EffectMeteor.Create(pos, 0, 1, delegate
			{ 
				EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("nino"), pos.x, pos.z).Chara); 
			}); 
			EClass.game.survival.flags.floors = 25; 
		} 
		else if (EClass.game.survival.flags.floors < 40 && i >= 40) 
		{ 
			EffectMeteor.Create(pos, 0, 1, delegate
			{ 
				EClass.pc.homeBranch.AddMemeber(EClass._zone.AddCard(CharaGen.Create("loytel"), pos.x, pos.z).Chara); 
			}); 
			EClass.game.survival.flags.floors = 40; 
		} 
		else if (EClass.game.survival.flags.floors < 50 && i >= 50) 
		{ 
			EffectMeteor.Create(pos, 0, 1, delegate
			{ 
				EClass._zone.AddCard(ThingGen.Create("core_defense"), pos).Install(); 
			}); 
			EClass.game.survival.flags.floors = 50; 
			EClass.game.survival.OnExpandFloor(pos); 
		}
	}

TaskDig

public override HitResult GetHitResult()

cs
			}
			return HitResult.Default;
		}
		if (EClass._zone.IsSkyLevel && (pos.Installed != null || pos.Charas.Count >= 2 || (pos.HasChara && pos.FirstChara != EClass.pc))) 
		if (EClass._zone.IsSkyLevel && (pos.IsSky || pos.Installed != null || pos.Charas.Count >= 2 || (pos.HasChara && (pos.FirstChara != EClass.pc || EClass.game.IsSurvival)))) 
		{
			return HitResult.Invalid;
		}

public override HitResult GetHitResult()

cs
		{
			return HitResult.Invalid;
		}
		if (!pos.HasBridge && pos.sourceFloor.id == 40) 
		if (!pos.HasBridge && !EClass._zone.IsSkyLevel && pos.sourceFloor.id == 40) 
		{
			return HitResult.Invalid;
		}

public override void OnProgressComplete()

cs
		{
			EClass.player.recipes.ComeUpWithRecipe(idRecipe, 30);
		}
		if (owner.IsPC && owner.IsAliveInCurrentZone && EClass._zone.IsSkyLevel && owner.pos.IsSky) 
		if (owner.IsPC && owner.IsAliveInCurrentZone && EClass._zone.IsSkyLevel && !EClass.game.IsSurvival && owner.pos.IsSky) 
		{
			EClass.pc.FallFromZone();
		}

TaskHarvest

public override bool Loop

cs
{
	get
	{
		if (EClass.game.Prologue.type == GameType.Survival && EClass._zone is Zone_StartSite) 
		if (EClass.game.IsSurvival && EClass._zone is Zone_StartSite) 
		{
			return CanProgress();
		}

TraitCoreDefense

public class TraitCoreDefense : Trait

cs

	public override void TrySetAct(ActPlan p)
	{
		if (EClass.game.IsSurvival && EClass._zone is Zone_StartSiteSky && !EClass.game.survival.flags.raid) 
		{ 
			p.TrySetAct("actWarhorn", delegate
			{ 
				EClass.game.survival.StartRaid(); 
				return true; 
			}); 
		} 
		ZoneEventDefenseGame ev = EClass._zone.events.GetEvent<ZoneEventDefenseGame>();
		if (ev == null)
		{

TraitGuildDoorman

public class TraitGuildDoorman : TraitUniqueGuildPersonnel

cs

	public virtual bool IsGuildMember => false;

	public override bool CanBePushed => IsGuildMember; 
	public override bool CanBePushed 
	{ 
		get 
		{ 
			if (!IsGuildMember) 
			{ 
				return base.owner.IsPCFaction; 
			} 
			return true; 
		} 
	} 

	public virtual void GiveTrial()
	{

+TraitVoidgate

File Created
cs
public class TraitVoidgate : Trait
{
	public override bool CanBeDestroyed => false;

	public override bool CanOnlyCarry => true;

	public override bool CanPutAway => false;

	public override bool IsAnimeOn => owner.isOn;
}

WidgetDate

public void _Refresh()

cs
	{
		text += item2.TextWidgetDate;
	}
	if (EMono.game.IsSurvival && EMono.game.survival.flags.raid) 
	{ 
		text += (EMono.game.survival.IsInRaid ? "raid_ongoing".lang() : "raid_till".lang(EMono.world.date.GetRemainingHours(EMono.game.survival.flags.dateNextRaid).ToString() ?? "")); 
	} 
	if (EMono.Branch != null && EMono.Branch.luckyMonth)
	{
		text = text + " " + "lucky_month".lang().TagColor(Msg.colors.colors["save"]);

+ZoneEventRaid

File Created
cs
public class ZoneEventRaid : ZoneEventSiege
{
	public override void OnFirstTick()
	{
		lv = 5 + EClass.game.survival.flags.raidRound * 10;
		max = 5 + lv / 5;
		base.OnFirstTick();
	}

	public override Point GetSpawnPos()
	{
		Trait trait = EClass._map.FindThing<TraitVoidgate>();
		if (trait != null)
		{
			return trait.owner.pos;
		}
		trait = EClass._map.FindThing<TraitCoreDefense>();
		if (trait != null)
		{
			return trait.owner.pos;
		}
		return EClass.pc.pos;
	}

	public override void OnKill()
	{
		base.OnKill();
		EClass.game.survival.flags.raidRound++;
		EClass.game.survival.flags.dateNextRaid = EClass.world.date.GetRaw(168);
	}
}

ZoneEventSiege

public class ZoneEventSiege : ZoneEvent

cs

	public List<Chara> members = new List<Chara>();

	public int lv = 10; 
	public int max = 10; 
	public override string id => "trial_siege";

	public override Playlist playlist => EClass.Sound.playlistBattle;

	public virtual Chara CreateChara()
	{
		return CharaGen.CreateFromFilter("c_wilds"); 
		return CharaGen.CreateFromFilter("c_wilds", lv); 
	}

	public override void OnFirstTick()

public override void OnFirstTick()

cs
	EClass.player.stats.sieges++;
	Msg.Say("startSiege");
	EClass._zone.RefreshBGM();
	Point randomEdge = EClass._map.GetRandomEdge(); 
	Point spawnPos = GetSpawnPos(); 
	for (int i = 0; i < 10; i++)
	{
		Chara chara = CreateChara();
		EClass._zone.AddCard(chara, EClass._map.GetRandomSurface(randomEdge.x, randomEdge.z, 6)); 
		EClass._zone.AddCard(chara, EClass._map.GetRandomSurface(spawnPos.x, spawnPos.z, 6)); 
		chara.hostility = Hostility.Enemy;
		members.Add(chara);
		uids.Add(chara.uid);
		chara.PlayEffect("teleport"); 
		chara.PlaySound("spell_funnel"); 
	}
	Thing t = ThingGen.Create("torch");
	EClass._zone.AddCard(t, randomEdge); 
	EClass._zone.AddCard(t, spawnPos); 
	if (members.Count != 0)
	{
		return;

public override void OnFirstTick()

cs
		}
	}

	public virtual Point GetSpawnPos() 
	{ 
		return EClass._map.GetRandomEdge(); 
	} 
	public override void OnTickRound()
	{
		bool flag = true;

public override void OnTickRound()

cs
			flag = false;
		}
	}
	if (flag || EClass.Branch.IsAllDead()) 
	if (flag) 
	{
		Kill();
	}

+ZoneEventSurvival

File Created
cs
using UnityEngine;

public class ZoneEventSurvival : ZoneEvent
{
	public override void OnTickRound()
	{
		Debug.Log("tick:" + rounds);
		Cell cell = EClass._map.cells[100, 100];
		if (!cell.HasObj)
		{
			EClass._map.SetObj(cell.x, cell.z, 46);
		}
		if (EClass.player.stats.days >= 10 && !EClass.game.survival.flags.raid)
		{
			EClass.game.survival.StartRaid();
		}
		if (EClass.game.survival.flags.raid)
		{
			TraitVoidgate traitVoidgate = EClass._map.FindThing<TraitVoidgate>();
			if (traitVoidgate != null)
			{
				traitVoidgate.owner.isOn = EClass.game.survival.IsInRaid;
			}
			if (!EClass.game.survival.IsInRaid && EClass.world.date.GetRemainingHours(EClass.game.survival.flags.dateNextRaid) <= 0)
			{
				EClass._zone.events.Add(new ZoneEventRaid());
			}
		}
	}
}

Zone_StartSite

public class Zone_StartSite : Zone

cs
	public override bool isClaimable => true;

	public override string IDBaseLandFeat => "bfPlain,bfFertile,bfStart";
	public override void OnGenerateMap() 
	{ 
	} 
}

Zone_User

public class Zone_User : Zone

cs

	public override bool RevealRoom => true;

	public override float OreChance => 0f; 
	public override bool IsUnderwater => elements.Has(3606);

	public override void OnActivate()