Skip to content

EA 23.48 Nightly

December 4, 2024

24 files modified.

Important Changes

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

Chara (1)

cs
public void Kick(Chara t, bool ignoreSelf = false, bool karmaLoss = true) 
public void Kick(Chara t, bool ignoreSelf = false, bool karmaLoss = true, bool show = true) 

Game (3)

cs
public static void Load(string slot) 
cs
public static void Load(string id, string root) 
public static void Load(string id, bool cloud) 
cs
public static void Create(string _id = null) 
public static void Create(string _id = null, bool cloud = false) 

GameIO (5)

cs
public static Game LoadGame(string id, string root) 
public static Game LoadGame(string id, string root, bool cloud) 
cs
public static bool FileExist(string id) 
cs
public static void DeleteGame(string id, bool deleteBackup = true) 
public static void DeleteGame(string id, bool cloud, bool deleteBackup = true) 
cs
public static void MakeDirectories(string id) 
cs
public static void DeleteEmptyGameFolders() 
public static void DeleteEmptyGameFolders(string path) 

LayerLoadGame (1)

cs
public void Init(bool _backup, string pathBackup = "", string _idDest = "") 
public void Init(bool _backup, string _pathBackup = "", string _idDest = "") 

AI_Fish

@@ -129,6 +129,10 @@ public override void OnProgressComplete()

cs
			{
				item.Destroy();
			}
			if (owner.things.IsFull()) 
			{ 
				thing.Destroy(); 
			} 
		}
		if (EClass.rnd(2) == 0 || num > 1)
		{

AI_PlayMusic

@@ -288,7 +288,7 @@ public override IEnumerable<Status> Run()

cs
								continue;
							}
						}
						if (EClass.rnd(num3 * num3) <= 30) 
						if (EClass.rnd(num3 * num3) <= 30 && item2.pos.FirstChara == item2) 
						{
							bool isMinion = item2.IsMinion;
							if (num2 < item2.LV && EClass.rnd(2) == 0)

@@ -502,17 +502,22 @@ public void ThrowReward(Chara c, bool punish)

cs
	{
		return;
	}
	owner.Pick(thing); 
	if (thing.id == "money" && !owner.IsPCParty) 
	if (!owner.IsPC && owner.things.IsFull()) 
	{
		if (thing.GetRootCard() != owner && !thing.isDestroyed) 
		{ 
			thing.Destroy(); 
		} 
		if (owner.GetCurrency() >= (owner.Evalue(241) * 10 + 100) / ((owner.IsPCFaction && owner.memberType == FactionMemberType.Default) ? 1 : 10)) 
		thing.Destroy(); 
	} 
	else
	{ 
		owner.Pick(thing); 
	} 
	if (thing.id == "money" && !owner.IsPC) 
	{ 
		int num2 = (owner.Evalue(241) * 10 + 100) / ((owner.IsPCFaction && owner.memberType == FactionMemberType.Default) ? 1 : 10); 
		int num3 = owner.GetCurrency() - num2; 
		if (num3 > 0) 
		{
			owner.c_allowance += num; 
			owner.ModCurrency(-num); 
			owner.c_allowance += num3; 
			owner.ModCurrency(-num3); 
		}
	}
}

AM_Adv

@@ -880,9 +880,10 @@ public override void _OnUpdateInput()

cs
		}
		EClass.core.WaitForEndOfFrame(delegate
		{
			string slot = Game.id; 
			string text = Game.id; 
			bool isCloud = EClass.game.isCloud; 
			EClass.scene.Init(Scene.Mode.None);
			Game.Load(slot); 
			Game.Load(text, isCloud); 
		});
		break;
	case EAction.Report:

ActPlan

@@ -500,46 +500,58 @@ public void _Update(PointTarget target)

cs
		}
		items.ForeachReverse(delegate(Card _c)
		{
			Chara chara = _c.Chara; 
			if (chara != null && !chara.IsPC && EClass.pc.CanSee(chara)) 
			Chara c = _c.Chara; 
			if (c != null && !c.IsPC && EClass.pc.CanSee(c)) 
			{
				int num = chara.Dist(EClass.pc); 
				int num = c.Dist(EClass.pc); 
				if (num <= 1 || !EClass.pc.isBlind)
				{
					if (!EClass.pc.isBlind && !chara.IsHostile() && (input == ActInput.AllAction || !(chara.IsPCParty || chara.IsMinion || isKey)) && (input == ActInput.AllAction || !chara.IsNeutral() || chara.quest != null || EClass.game.quests.IsDeliverTarget(chara)) && chara.isSynced && num <= 2) 
					if (!EClass.pc.isBlind && !c.IsHostile() && (input == ActInput.AllAction || !(c.IsPCParty || c.IsMinion || isKey)) && (input == ActInput.AllAction || !c.IsNeutral() || c.quest != null || EClass.game.quests.IsDeliverTarget(c)) && c.isSynced && num <= 2) 
					{
						bool flag = !chara.HasCondition<ConSuspend>() && (!chara.isRestrained || !chara.IsPCFaction); 
						if (EClass._zone.instance is ZoneInstanceMusic && !chara.IsPCFactionOrMinion) 
						bool flag = !c.HasCondition<ConSuspend>() && (!c.isRestrained || !c.IsPCFaction); 
						if (EClass._zone.instance is ZoneInstanceMusic && !c.IsPCFactionOrMinion) 
						{
							flag = false;
						}
						if (flag || altAction)
						{
							if (EClass.pc.HasElement(1216) && chara.HasCondition<ConSleep>()) 
							if (EClass.pc.HasElement(1216) && c.HasCondition<ConSleep>()) 
							{
								TrySetAct(new AI_Fuck
								{
									target = chara, 
									target = c, 
									succubus = true
								}, chara); 
								}, c); 
							}
							TrySetAct(ACT.Chat, chara); 
							TrySetAct(ACT.Chat, c); 
						}
					}
					if (chara.host != EClass.pc) 
					if (c.host != EClass.pc) 
					{
						if (chara.IsRestrainedResident) 
						if (c.IsRestrainedResident) 
						{
							TrySetAct(new AI_PracticeDummy
							{
								target = chara 
								target = c 
							});
						}
						else if ((chara.IsHostile() || altAction || chara.isRestrained) && chara.IsAliveInCurrentZone) 
						else if ((c.IsHostile() || altAction || c.isRestrained) && c.IsAliveInCurrentZone) 
						{
							TrySetAct(ACT.Melee, chara); 
							TrySetAct(ACT.Melee, c); 
						}
					}
					if (c.IsPCPartyMinion && !c.Chara.IsEscorted() && altAction) 
					{ 
						TrySetAct("ActBanishSummon", delegate
						{ 
							EClass.pc.Say("summon_vanish", c); 
							c.pos.PlayEffect("vanish"); 
							c.pos.PlaySound("vanish"); 
							c.pos.PlayEffect("teleport"); 
							c.Destroy(); 
							return true; 
						}, c, null, 99); 
					} 
				}
			}
		});

@@ -549,15 +561,15 @@ public void _Update(PointTarget target)

cs
		}
		items.ForeachReverse(delegate(Card _c)
		{
			Chara c = _c.Chara; 
			if (c != null) 
			Chara c2 = _c.Chara; 
			if (c2 != null) 
			{
				bool flag2 = EClass.pc.CanSee(c); 
				bool flag2 = EClass.pc.CanSee(c2); 
				if (flag2)
				{
					if (input == ActInput.LeftMouse && c.IsPCFaction && !c.IsPC && pos.FindThing<TraitHitchingPost>() != null) 
					if (input == ActInput.LeftMouse && c2.IsPCFaction && !c2.IsPC && pos.FindThing<TraitHitchingPost>() != null) 
					{
						Chara ride = c; 
						Chara ride = c2; 
						List<string> list = EClass.core.pccs.sets["ride"].map["body"].map.Keys.ToList();
						int index = list.IndexOf(ride.c_idRidePCC);
						if (index == -1)

@@ -574,35 +586,35 @@ public void _Update(PointTarget target)

cs
							}, 0f, list.Count - 1, isInt: true, hideOther: false);
							uIContextMenu.Show();
							return false;
						}, c); 
						}, c2); 
					}
					if (!c.IsPC && ((c.IsPCFaction && !c.IsDisabled) || EClass.debug.enable) && input == ActInput.AllAction) 
					if (!c2.IsPC && ((c2.IsPCFaction && !c2.IsDisabled) || EClass.debug.enable) && input == ActInput.AllAction) 
					{
						TrySetAct("actTrade", delegate
						{
							LayerInventory.CreateContainer(c); 
							LayerInventory.CreateContainer(c2); 
							return false;
						}, c); 
						}, c2); 
					}
					if (c.host != null && EClass.pc.held != null && altAction) 
					if (c2.host != null && EClass.pc.held != null && altAction) 
					{
						bool flag3 = true;
						if ((EClass.pc.held.trait is TraitThrown || EClass.pc.held.trait.IsTool) && !HotItemHeld.disableTool)
						{
							flag3 = false;
						}
						if (!c.IsDisabled && flag3 && c.CanAcceptGift(EClass.pc, EClass.pc.held)) 
						if (!c2.IsDisabled && flag3 && c2.CanAcceptGift(EClass.pc, EClass.pc.held)) 
						{
							string lang = "actGive";
							if (c.Evalue(1232) > 0 && EClass.pc.held.trait is TraitDrinkMilkMother) 
							if (c2.Evalue(1232) > 0 && EClass.pc.held.trait is TraitDrinkMilkMother) 
							{
								lang = "actMilk";
							}
							TrySetAct(lang, delegate
							{
								if (!c.IsValidGiftWeight(EClass.pc.held, 1)) 
								if (!c2.IsValidGiftWeight(EClass.pc.held, 1)) 
								{
									c.Talk("tooHeavy"); 
									c2.Talk("tooHeavy"); 
									return true;
								}
								if (EClass.core.config.game.confirmGive)

@@ -614,58 +626,46 @@ public void _Update(PointTarget target)

cs
									func();
								}
								return true;
							}, c); 
							}, c2); 
						}
					}
				}
				if (input == ActInput.AllAction && EClass.pc.held != null && EClass.pc.held.trait is TraitDrink)
				{
					TrySetAct(c.IsPC ? "actPour" : "ActThrow", delegate
					TrySetAct(c2.IsPC ? "actPour" : "ActThrow", delegate 
					{
						ActThrow.Throw(EClass.pc, c.pos, c, EClass.pc.held.Split(1)); 
						ActThrow.Throw(EClass.pc, c2.pos, c2, EClass.pc.held.Split(1)); 
						return true;
					}, (c.host != null) ? c : EClass.pc.held); 
					}, (c2.host != null) ? c2 : EClass.pc.held); 
				}
				if (!c.IsPC && c.host == null && ((!EClass.pc.isBlind && flag2) || input != ActInput.AllAction)) 
				if (!c2.IsPC && c2.host == null && ((!EClass.pc.isBlind && flag2) || input != ActInput.AllAction)) 
				{
					if (c.isRestrained && (input == ActInput.AllAction || (!c.IsRestrainedResident && !c.IsHostile()))) 
					if (c2.isRestrained && (input == ActInput.AllAction || (!c2.IsRestrainedResident && !c2.IsHostile()))) 
					{
						TrySetAct("ActUnrestrain", delegate
						{
							c.TryUnrestrain(force: true, EClass.pc); 
							c2.TryUnrestrain(force: true, EClass.pc); 
							return true;
						}, c); 
						}, c2); 
					}
					if (!EClass.pc.isBlind && flag2 && input == ActInput.AllAction)
					{
						TrySetAct(ACT.Kick, c); 
						if (c.IsMofuable) 
						TrySetAct(ACT.Kick, c2); 
						if (c2.IsMofuable) 
						{
							TrySetAct("ActCuddle", delegate
							{
								EClass.pc.Cuddle(c); 
								EClass.pc.Cuddle(c2); 
								return true;
							}, c); 
							}, c2); 
						}
						if (EClass.debug.showExtra)
						{
							TrySetAct("inspect", delegate
							{
								c.Inspect(); 
								c2.Inspect(); 
								return false;
							}, c); 
						} 
						if (c.IsPCPartyMinion && !c.Chara.IsEscorted()) 
						{ 
							TrySetAct("ActBanishSummon", delegate
							{ 
								EClass.pc.Say("summon_vanish", c); 
								c.pos.PlayEffect("vanish"); 
								c.pos.PlaySound("vanish"); 
								c.pos.PlayEffect("teleport"); 
								c.Destroy(); 
								return true; 
							}, c); 
							}, c2); 
						}
					}
				}

@@ -765,7 +765,7 @@ public void _Update(PointTarget target)

cs
			}
			void func()
			{
				EClass.pc.GiveGift(c, EClass.pc.SplitHeld(1) as Thing); 
				EClass.pc.GiveGift(c2, EClass.pc.SplitHeld(1) as Thing); 
			}
		});
		if (listPick.Count > 0)

ButtonAbility

@@ -182,38 +182,31 @@ public static bool TryUse(Act act, Card tg = null, Point pos = null, Card cataly

cs

	public static bool SpecialHoldAction(Act act)
	{
		Act e = EClass.pc.elements.GetElement(act.id) as Act; 
		if (e == null) 
		if (!(EClass.pc.elements.GetElement(act.id) is Act { id: var id } act2)) 
		{
			return false;
		}
		int id = e.id; 
		if (id == 8230 || id == 8232)
		{
			bool stop = false; 
			bool first = true; 
			int count = 0; 
			EClass.pc.things.Foreach(delegate(Thing t) 
			bool flag = true; 
			int num = 0; 
			foreach (Thing item in EClass.pc.things.List((Thing t) => true, onlyAccessible: true)) 
			{
				if (!t.IsIdentified) 
				if (!item.IsIdentified) 
				{
					if (EClass.pc.mana.value < e.GetCost(EClass.pc).cost && !first) 
					if ((EClass.pc.mana.value < act2.GetCost(EClass.pc).cost && !flag) || act2.vPotential <= 0) 
					{
						stop = true; 
						break; 
					}
					if (e.vPotential <= 0) 
					if (item.rarity < Rarity.Mythical || act2.id != 8230) 
					{
						stop = true; 
					} 
					if (!stop && (t.rarity < Rarity.Mythical || e.id != 8230)) 
					{ 
						EClass.pc.UseAbility(act.source.alias, t, EClass.pc.pos); 
						count++; 
						first = false; 
						EClass.pc.UseAbility(act.source.alias, item, EClass.pc.pos); 
						num++; 
						flag = false; 
					}
				}
			}); 
			if (count == 0) 
			} 
			if (num == 0) 
			{
				Msg.Say("identify_nothing");
			}

Card

@@ -4560,6 +4560,10 @@ bool chance(int i)

cs
		{
			i = 1;
		}
		if (IsMinion) 
		{ 
			i *= 2; 
		} 
		if (EClass.rnd(i) == 0)
		{
			return true;

Chara

@@ -1676,6 +1676,10 @@ public void RefreshSpeed(Element.BonusInfo info = null)

cs
			info?.AddFix(EClass.player.lastEmptyAlly * 10 - 10, "exceedParty".lang());
		}
	}
	else if (base.LV >= 1000) 
	{ 
		num += EClass.curve((base.LV - 900) / 100 * 10, 500, 100); 
	} 
	if (HasCondition<ConGravity>())
	{
		num -= 30;

@@ -4949,7 +4953,7 @@ public void Kick(Point p, bool ignoreSelf = false)

cs
		}
	}

	public void Kick(Chara t, bool ignoreSelf = false, bool karmaLoss = true) 
	public void Kick(Chara t, bool ignoreSelf = false, bool karmaLoss = true, bool show = true) 
	{
		if (!IsAliveInCurrentZone)
		{

@@ -4975,7 +4979,10 @@ public void Kick(Chara t, bool ignoreSelf = false, bool karmaLoss = true)

cs
		}
		return;
	}
	Say("kick", this, t); 
	if (show) 
	{ 
		Say("kick", this, t); 
	} 
	PlaySound("kick");
	if ((t.conSuspend == null || t.conSuspend.uidMachine != 0) && t.trait.CanBePushed && (!t.IsHostile() || EClass.rnd(2) == 0) && !t.noMove && !t.isRestrained)
	{

Core

@@ -482,7 +482,7 @@ public void OnApplicationQuit()

cs
	try
	{
		IO.DeleteDirectory(CorePath.Temp);
		GameIO.DeleteEmptyGameFolders(); 
		GameIO.DeleteEmptyGameFolders(CorePath.RootSave); 
	}
	catch (Exception ex3)
	{

CoreConfig

@@ -571,6 +571,8 @@ public class Fix

cs

	public bool ignoreLinuxModWarning;

	public bool cloud; 

	public new UISetting ui;

	public SoundSetting sound;

CoreDebug

@@ -974,19 +974,20 @@ public void UpdateInput()

cs
		string text2 = (Input.GetKey(KeyCode.LeftControl) ? "quick3" : (Input.GetKey(KeyCode.LeftShift) ? "quick2" : "quick"));
		if (text2 != Game.id)
		{
			IO.DeleteDirectory(GameIO.pathSaveRoot + text2); 
			IO.CopyAll(GameIO.pathSaveRoot + Game.id, GameIO.pathSaveRoot + text2); 
			IO.DeleteDirectory(CorePath.RootSave + text2); 
			IO.CopyAll(CorePath.RootSave + Game.id, CorePath.RootSave + text2); 
			Game.id = text2;
		}
		EClass.game.isCloud = false; 
		EClass.game.Save();
	}
	if (Input.GetKeyDown(KeyCode.F6))
	{
		EClass.core.WaitForEndOfFrame(delegate
		{
			string slot = (Input.GetKey(KeyCode.LeftControl) ? "quick3" : (Input.GetKey(KeyCode.LeftShift) ? "quick2" : "quick")); 
			string id = (Input.GetKey(KeyCode.LeftControl) ? "quick3" : (Input.GetKey(KeyCode.LeftShift) ? "quick2" : "quick")); 
			EClass.scene.Init(Scene.Mode.None);
			Game.Load(slot); 
			Game.Load(id, cloud: false); 
		});
	}
	if (Input.GetKeyDown(KeyCode.F7))

Game

@@ -230,6 +230,8 @@ private void _OnDeserialized(StreamingContext context)

cs

	public bool isKilling;

	public bool isCloud; 

	public GameBlueprint bp;

	public GameUpdater updater = new GameUpdater();

@@ -293,21 +295,19 @@ public void OnUpdate()

cs
		updater.FixedUpdate();
	}

	public static void Load(string slot) 
	{ 
		Load(slot, GameIO.pathSaveRoot + slot); 
	} 

	public static void Load(string id, string root) 
	public static void Load(string id, bool cloud) 
	{
		Debug.Log("Loading: " + id + ": " + root); 
		string text = (cloud ? CorePath.RootSaveCloud : CorePath.RootSave) + id; 
		Debug.Log("Loading: " + id + ": " + text); 
		if (EClass.game != null)
		{
			EClass.game.Kill();
		}
		OnBeforeInstantiate();
		EClass.core.game = GameIO.LoadGame(id, root); 
		EClass.core.game = GameIO.LoadGame(id, text, cloud); 
		EClass.game.isCloud = cloud; 
		EClass.game.isLoading = true;
		GameIO.ClearTemp(); 
		EClass.game.OnGameInstantiated();
		EClass.game.OnLoad();
		EClass.scene.Init(Scene.Mode.StartGame);

@@ -551,12 +551,13 @@ void TryAddQuestIfActive(string idQuest, string idReqQuest)

cs
		}
	}

	public static void Create(string _id = null) 
	public static void Create(string _id = null, bool cloud = false) 
	{
		id = _id ?? GameIO.GetNewId(GameIO.pathSaveRoot, "world_"); 
		GameIO.ResetTemp(); 
		id = _id ?? GameIO.GetNewId(cloud ? CorePath.RootSaveCloud : CorePath.RootSave, "world_"); 
		OnBeforeInstantiate();
		EClass.core.game = (Instance = new Game());
		EClass.core.game.isCloud = cloud; 
		GameIO.ResetTemp(); 
		EClass.core.game.OnGameInstantiated();
		EClass.core.game._Create();
	}

GameIO

@@ -1,6 +1,7 @@

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

@@ -28,11 +29,7 @@ public class GameIO : EClass

cs

	public static Formatting formatting = Formatting.Indented;

	public static string pathBackup => CorePath.RootSave + "Backup/"; 

	public static string pathSaveRoot => CorePath.RootSave; 

	public static string pathCurrentSave => pathSaveRoot + Game.id + "/"; 
	public static string pathCurrentSave => (EClass.core.game.isCloud ? CorePath.RootSaveCloud : CorePath.RootSave) + Game.id + "/"; 

	public static string pathTemp => pathCurrentSave + "Temp/";

@@ -84,6 +81,7 @@ public static GameIndex SaveGame()

cs
	string path = pathCurrentSave + "game.txt";
	GameIndex gameIndex = new GameIndex().Create(EClass.core.game);
	gameIndex.id = Game.id;
	gameIndex.cloud = EClass.game.isCloud; 
	IO.SaveFile(pathCurrentSave + "index.txt", gameIndex);
	if (compressSave)
	{

@@ -103,6 +101,10 @@ public static GameIndex SaveGame()

cs
			}
		}
		ClearTemp();
		if (gameIndex.cloud) 
		{ 
			PrepareSteamCloud(gameIndex.id); 
		} 
		return gameIndex;
	}

@@ -110,8 +112,9 @@ public static void MakeBackup(GameIndex index, string suffix = "")

cs
{
	Debug.Log("Start backup:" + index.id);
	string id = index.id;
	IO.CreateDirectory(pathBackup); 
	string text = pathBackup + id; 
	bool cloud = index.cloud; 
	IO.CreateDirectory(cloud ? CorePath.PathBackupCloud : CorePath.PathBackup); 
	string text = (cloud ? CorePath.PathBackupCloud : CorePath.PathBackup) + id; 
	IO.CreateDirectory(text);
	Debug.Log(text);
	List<DirectoryInfo> dirs = new DirectoryInfo(text).GetDirectories().ToList();

@@ -134,20 +137,99 @@ public static void MakeBackup(GameIndex index, string suffix = "")

cs
		}
		Debug.Log("Copying backup:");
		string newId = GetNewId(text + "/", "", (dirs.Count == 0) ? 1 : int.Parse(dirs.LastItem().Name));
		IO.CopyDir(pathSaveRoot + id + "/", text + "/" + newId, (string s) => s == "Temp"); 
		IO.CopyDir((cloud ? CorePath.RootSaveCloud : CorePath.RootSave) + id + "/", text + "/" + newId, (string s) => s == "Temp"); 
	}

	public static Game LoadGame(string id, string root) 
	public static Game LoadGame(string id, string root, bool cloud) 
	{
		Game.id = id;
		ClearTemp(); 
		GameIndex gameIndex = IO.LoadFile<GameIndex>(root + "/index.txt"); 
		if (cloud) 
		{ 
			gameIndex.cloud = true; 
			Debug.Log(TryLoadSteamCloud()); 
		} 
		string path = root + "/game.txt";
		return JsonConvert.DeserializeObject<Game>(IO.IsCompressed(path) ? IO.Decompress(path) : File.ReadAllText(path), jsReadGame);
	}

	public static void PrepareSteamCloud(string id, string path = "") 
	{ 
		if (path.IsEmpty()) 
		{ 
			path = CorePath.RootSaveCloud + "/" + id; 
		} 
		Debug.Log("Prepareing Steam Cloud:" + id + ": " + path); 
		string text = CorePath.RootSaveCloud + "/cloud.zip"; 
		string text2 = path + "/cloud.zip"; 
		try
		{ 
			if (File.Exists(text)) 
			{ 
				File.Delete(text); 
			} 
			ZipFile.CreateFromDirectory(path, text); 
			if (File.Exists(text2)) 
			{ 
				File.Delete(text2); 
			} 
			File.Move(text, text2); 
		} 
		catch (Exception ex) 
		{ 
			EClass.ui.Say(ex.Message); 
		} 
	} 

	public static bool TryLoadSteamCloud() 
	{ 
		Debug.Log("LoadGame using cloud save"); 
		string text = CorePath.RootSaveCloud + Game.id; 
		string text2 = text + "/cloud.zip"; 
		string text3 = CorePath.RootSaveCloud + "/cloud.zip"; 
		bool flag = false; 
		try
		{ 
			if (!File.Exists(text2)) 
			{ 
				EClass.ui.Say("Steam Cloud save not found:" + text2); 
				return true; 
			} 
			if (File.Exists(text3)) 
			{ 
				File.Delete(text3); 
			} 
			File.Move(text2, text3); 
			IO.DeleteDirectory(text); 
			flag = true; 
			Directory.CreateDirectory(text); 
			ZipFile.ExtractToDirectory(text3, text); 
			if (File.Exists(text2)) 
			{ 
				File.Delete(text2); 
			} 
			File.Move(text3, text2); 
		} 
		catch (Exception ex) 
		{ 
			EClass.ui.Say(ex.Message); 
			if (flag) 
			{ 
				Debug.Log("Try restore backup:"); 
				if (Directory.Exists(text)) 
				{ 
					Directory.Delete(text); 
				} 
				File.Move(text3, text2); 
				return true; 
			} 
			return false; 
		} 
		return true; 
	} 

	public static void UpdateGameIndex(GameIndex i)
	{
		i.madeBackup = true; 
		IO.SaveFile(i.path + "/index.txt", i);
	}

@@ -161,25 +243,21 @@ public static void SaveFile(string path, object obj)

cs
		return IO.LoadFile<T>(path, compressSave, jsReadGame);
	}

	public static bool FileExist(string id) 
	{ 
		return File.Exists(pathSaveRoot + Game.id + "/" + id + ".txt"); 
	} 

	public static void DeleteGame(string id, bool deleteBackup = true) 
	public static void DeleteGame(string id, bool cloud, bool deleteBackup = true) 
	{
		if (!Directory.Exists(pathSaveRoot + id)) 
		string path = (cloud ? CorePath.RootSaveCloud : CorePath.RootSave) + id; 
		if (!Directory.Exists(path)) 
		{
			return;
		}
		DirectoryInfo directoryInfo = new DirectoryInfo(pathSaveRoot + id); 
		DirectoryInfo directoryInfo = new DirectoryInfo(path); 
		if (directoryInfo.Exists)
		{
			directoryInfo.Delete(recursive: true);
		}
		if (deleteBackup)
		{
			directoryInfo = new DirectoryInfo(pathBackup + id); 
			directoryInfo = new DirectoryInfo((cloud ? CorePath.PathBackupCloud : CorePath.PathBackup) + id); 
			if (directoryInfo.Exists)
			{
				directoryInfo.Delete(recursive: true);

@@ -187,18 +265,6 @@ public static void DeleteGame(string id, bool deleteBackup = true)

cs
		}
	}

	public static void MakeDirectories(string id) 
	{ 
		if (!Directory.Exists(pathSaveRoot + id)) 
		{ 
			Directory.CreateDirectory(pathSaveRoot + id); 
		} 
		if (!Directory.Exists(pathSaveRoot + id + "/Temp")) 
		{ 
			Directory.CreateDirectory(pathSaveRoot + id + "/Temp"); 
		} 
	} 

	public static List<GameIndex> GetGameList(string path, bool sortByName = false)
	{
		List<GameIndex> list = new List<GameIndex>();

@@ -240,12 +306,12 @@ public static List<GameIndex> GetGameList(string path, bool sortByName = false)

cs
		return list;
	}

	public static void DeleteEmptyGameFolders() 
	public static void DeleteEmptyGameFolders(string path) 
	{
		DirectoryInfo[] directories = new DirectoryInfo(pathSaveRoot).GetDirectories(); 
		DirectoryInfo[] directories = new DirectoryInfo(path).GetDirectories(); 
		foreach (DirectoryInfo directoryInfo in directories)
		{
			if (directoryInfo.Name != "Backup" && !File.Exists(directoryInfo?.ToString() + "/game.txt")) 
			if (!File.Exists(directoryInfo?.ToString() + "/game.txt")) 
			{
				directoryInfo.Delete(recursive: true);
			}

GameIndex

@@ -39,6 +39,8 @@ public class GameIndex : EClass

cs

	public bool isBackup;

	public bool cloud; 

	[JsonIgnore]
	public string path;

LayerFeedback

@@ -133,7 +133,7 @@ void Append(string text)

cs
		CollectFiles();
	};
	category.Init((string s) => ("form_" + s).lang());
	List<GameIndex> gameList = GameIO.GetGameList(GameIO.pathSaveRoot); 
	List<GameIndex> gameList = GameIO.GetGameList((ELayer.core.config.cloud || (ELayer.core.IsGameStarted && ELayer.game.isCloud)) ? CorePath.RootSaveCloud : CorePath.RootSave); 
	if (gameList.Count > 0)
	{
		int index = 0;

@@ -234,11 +234,12 @@ public void CollectFiles()

cs
	form.CurrentReport.AttachFile("log.zip", File.ReadAllBytes(text3));
	if (toggleSave.isOn)
	{
		bool flag = ELayer.core.config.cloud || (ELayer.core.IsGameStarted && ELayer.game.isCloud); 
		string text5 = text2 + "/save.zip";
		using (ZipFile zipFile2 = new ZipFile())
		{
			zipFile2.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently;
			zipFile2.AddDirectory(GameIO.pathSaveRoot + saveIndex.id); 
			zipFile2.AddDirectory((flag ? CorePath.RootSaveCloud : CorePath.RootSave) + saveIndex.id); 
			zipFile2.Save(text5);
		}
		form.CurrentReport.AttachFile(saveIndex.id + ".zip", File.ReadAllBytes(text5));

LayerLoadGame

@@ -24,6 +24,8 @@ public class LayerLoadGame : ELayer

cs

	public UIButton buttonListBackup;

	public UIButton buttonMove; 

	public List<GameIndex> worlds;

	public Portrait portrait;

@@ -46,13 +48,17 @@ public class LayerLoadGame : ELayer

cs

	private string idDest;

	private string pathBackup; 

	private bool backup;

	public void Init(bool _backup, string pathBackup = "", string _idDest = "") 
	private bool cloud => ELayer.core.config.cloud; 

	public void Init(bool _backup, string _pathBackup = "", string _idDest = "") 
	{
		backup = _backup;
		pathBackup = _pathBackup; 
		idDest = _idDest;
		pathRoot = (backup ? pathBackup : GameIO.pathSaveRoot); 
		if (backup)
		{
			for (int i = 0; i < 2; i++)

@@ -81,14 +87,26 @@ public void Init(bool _backup, string pathBackup = "", string _idDest = "")

cs
			}
		}
		RefreshList();
		if (!backup) 
		{ 
			windows[0].ClearBottomButtons(); 
			windows[0].AddBottomButton("toggleCloud", delegate
			{ 
				ELayer.core.config.cloud = !ELayer.core.config.cloud; 
				SE.Tab(); 
				RefreshList(); 
			}); 
		} 
	}

	public void RefreshList()
	{
		if (worlds == null) 
		if (!backup) 
		{
			worlds = GameIO.GetGameList(pathRoot, backup); 
			windows[0].SetCaption("saveList".lang() + (cloud ? (" " + "isCloud".lang()) : "")); 
		}
		pathRoot = (backup ? pathBackup : (cloud ? CorePath.RootSaveCloud : CorePath.RootSave)); 
		worlds = GameIO.GetGameList(pathRoot, backup); 
		goInfo.SetActive(value: false);
		goNoInfo.SetActive(value: true);
		list.Clear();

@@ -101,7 +119,7 @@ public void RefreshList()

cs
			b.mainText.SetText(s, c);
			b.subText.SetText(a.RealDate ?? "");
			b.subText2.SetText(((a.difficulty == 2) ? "★" : ((a.difficulty == 1) ? "☆" : "")) + a.pcName + " (" + a.zoneName + ")", c);
			b.GetComponent<UIItem>().text1.SetText(a.version.GetText() ?? ""); 
			b.GetComponent<UIItem>().text1.SetText(a.version.GetText() + (cloud ? (" " + "isCloud".lang()) : "")); 
		},
		onClick = delegate(GameIndex a, UIButton b)
		{

@@ -162,6 +180,66 @@ public void RefreshInfo(GameIndex i)

cs
	buttonDelete.SetActive(!backup && !ELayer.core.IsGameStarted);
	buttonBackup.SetActive(!backup && (!ELayer.core.IsGameStarted || i.id == Game.id));
	buttonOpen.SetActive(backup);
	buttonMove.SetActive(!backup && !ELayer.core.IsGameStarted); 
	buttonMove.mainText.SetText((cloud ? "fromCloud" : "toCloud").lang()); 
	buttonMove.SetOnClick(delegate
	{ 
		Dialog.YesNo("dialog_switchCloud", delegate
		{ 
			string sourceDirName = (cloud ? CorePath.RootSaveCloud : CorePath.RootSave) + i.id; 
			string text = (cloud ? CorePath.RootSave : CorePath.RootSaveCloud) + i.id; 
			string text2 = (cloud ? CorePath.PathBackupCloud : CorePath.PathBackup) + i.id; 
			string text3 = (cloud ? CorePath.PathBackup : CorePath.PathBackupCloud) + i.id; 
			bool flag2 = Directory.Exists(text2); 
			if (Directory.Exists(text) || Directory.Exists(text3)) 
			{ 
				SE.Beep(); 
				ELayer.ui.Say("cloud_conflict"); 
			} 
			else
			{ 
				SE.Play("mutation"); 
				try
				{ 
					if (flag2) 
					{ 
						Debug.Log("Converting Backup files:"); 
						Directory.Move(text2, text3); 
						foreach (GameIndex game in GameIO.GetGameList(((!cloud) ? CorePath.PathBackupCloud : CorePath.PathBackup) + i.id + "/")) 
						{ 
							Debug.Log("Processing:" + game.id + ": " + game.path); 
							if (cloud) 
							{ 
								IO.DeleteFile(game.path + "/cloud.zip"); 
							} 
							else
							{ 
								GameIO.PrepareSteamCloud(game.id, game.path); 
							} 
						} 
					} 
					Debug.Log("Converting Current World:"); 
					Directory.Move(sourceDirName, text); 
					i.path = text; 
					i.cloud = !cloud; 
					GameIO.UpdateGameIndex(i); 
					if (i.cloud) 
					{ 
						GameIO.PrepareSteamCloud(i.id); 
					} 
					else
					{ 
						IO.DeleteFile(i.path + "/cloud.zip"); 
					} 
				} 
				catch (Exception ex) 
				{ 
					ELayer.ui.Say(ex.Message); 
				} 
				RefreshList(); 
			} 
		}); 
	}); 
	buttonLoad.onClick.RemoveAllListeners();
	buttonDelete.onClick.RemoveAllListeners();
	buttonLoad.SetOnClick(delegate

@@ -171,11 +249,11 @@ public void RefreshInfo(GameIndex i)

cs
		{
			Dialog.YesNo("dialog_restoreWarning", delegate
			{
				GameIO.DeleteGame(idDest, deleteBackup: false); 
				IO.CopyDir(pathRoot + "/" + i.id, GameIO.pathSaveRoot + "/" + idDest); 
				GameIO.DeleteGame(idDest, cloud, deleteBackup: false); 
				IO.CopyDir(pathRoot + "/" + i.id, (cloud ? CorePath.RootSaveCloud : CorePath.RootSave) + "/" + idDest); 
				SE.WriteJournal();
				Close();
				Game.Load(idDest); 
				Game.Load(idDest, cloud); 
			});
		}
		else

@@ -184,16 +262,17 @@ public void RefreshInfo(GameIndex i)

cs
			{
				GameIO.MakeBackup(i);
				ELayer.ui.Say("backupDone");
				i.madeBackup = true; 
				GameIO.UpdateGameIndex(i);
			}
			Game.Load(i.id); 
			Game.Load(i.id, cloud); 
		}
	});
	buttonDelete.SetOnClick(delegate
	{
		Dialog.YesNo("dialogDeleteGame", delegate
		{
			GameIO.DeleteGame(i.id); 
			GameIO.DeleteGame(i.id, cloud); 
			worlds = null;
			RefreshList();
			SE.Trash();

@@ -201,7 +280,7 @@ public void RefreshInfo(GameIndex i)

cs
	});
	buttonListBackup.SetOnClick(delegate
	{
		ELayer.ui.AddLayer<LayerLoadGame>().Init(_backup: true, GameIO.pathBackup + i.id + "/", i.id); 
		ELayer.ui.AddLayer<LayerLoadGame>().Init(_backup: true, (cloud ? CorePath.PathBackupCloud : CorePath.PathBackup) + i.id + "/", i.id); 
	});
	buttonBackup.SetOnClick(delegate
	{

@@ -210,6 +289,7 @@ public void RefreshInfo(GameIndex i)

cs
			ELayer.game.backupTime = 0.0;
			ELayer.game.Save();
		}
		i.cloud = cloud; 
		GameIO.MakeBackup(i);
		ELayer.ui.Say("backupDone");
		SE.WriteJournal();

Map

@@ -311,7 +311,7 @@ public void Reload()

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

Recipe

@@ -608,7 +608,7 @@ public virtual void Build(Chara chara, Card t, Point pos, int mat, int dir, int

cs
		{
			foreach (Chara item in pos.ListCharas())
			{
				chara.Kick(item); 
				chara.Kick(item, ignoreSelf: false, karmaLoss: false); 
			}
		}
		if (tileType.AltitudeAsDir)

RecipeCard

@@ -441,7 +441,7 @@ void CheckBlock(Point _pos)

cs
		{
			foreach (Chara item in _pos.ListCharas())
			{
				chara.Kick(item); 
				chara.Kick(item, ignoreSelf: false, karmaLoss: false); 
			}
		}
	}

Scene

@@ -558,7 +558,7 @@ public void OnUpdate()

cs
			string lastWord = list.RandomItem();
			if (EMono.game.Difficulty.deleteGameOnDeath)
			{
				GameIO.DeleteGame(Game.id); 
				GameIO.DeleteGame(Game.id, EMono.game.isCloud); 
			}
			EMono.ui.CloseLayers();
			if ((bool)UIContextMenu.Current)

TaskBuild

@@ -315,13 +315,20 @@ public override void OnProgressComplete()

cs
		EClass._map.RefreshShadow(pos.x, pos.z - 1);
		EClass._map.RefreshFOV(pos.x, pos.z);
		EClass.pc.renderer.SetFirst(first: true);
		if (!recipe.IsFloor) 
		if (recipe.IsFloor) 
		{ 
			foreach (Card item in pos.ListThings<TraitNewZone>()) 
			{ 
				_ = (item.trait as TraitNewZone).IsDownstairs; 
			} 
		} 
		if (!pos.IsBlocked || !pos.HasChara) 
		{
			return;
		}
		foreach (Card item in pos.ListThings<TraitNewZone>()) 
		foreach (Chara item2 in pos.ListCharas()) 
		{
			_ = (item.trait as TraitNewZone).IsDownstairs; 
			EClass.pc.Kick(item2, ignoreSelf: true, karmaLoss: false, show: false); 
		}
	}

TraitSyringeGene

@@ -32,7 +32,7 @@ public override void TrySetHeldAct(ActPlan p)

cs
				}
				owner.ModNum(-1);
				return false;
			}); 
			}, c); 
		});
	}
}

TraitSyringeHeaven

@@ -20,7 +20,7 @@ public override void TrySetHeldAct(ActPlan p)

cs
				}
				owner.ModNum(-1);
				return false;
			}); 
			}, c); 
		});
	}
}

Zone

@@ -880,7 +880,7 @@ public void Activate()

cs
			}
			if (card.pos.cell.IsBlocked && !card.isRestrained && !card.HasCondition<ConSuspend>())
			{
				card.MoveImmediate(card.pos.GetNearestPoint() ?? card.pos); 
				card.MoveImmediate(card.pos.GetNearestPoint(allowBlock: false, allowChara: false) ?? card.pos); 
			}
			chara.SyncRide();
			if (card.c_uidMaster != 0 && chara.master == null)