diff --git a/Slipspace Engine/modinfo.sbmi b/Slipspace Engine/modinfo.sbmi new file mode 100644 index 00000000..8b4e7c31 --- /dev/null +++ b/Slipspace Engine/modinfo.sbmi @@ -0,0 +1,11 @@ + + + 76561198071098415 + 0 + + + 3260158135 + Steam + + + \ No newline at end of file diff --git a/TSTSSESCores/Data/Guids.sbc b/TSTSSESCores/Data/Guids.sbc new file mode 100644 index 00000000..65398e06 --- /dev/null +++ b/TSTSSESCores/Data/Guids.sbc @@ -0,0 +1,15 @@ + + + + + + ModStorageComponent + MIG-SpecCores + + + 82f6e121-550f-4042-a2b8-185ed2a52abc + 82f6e121-550f-4042-a2b8-185ed2a52abd + + + + \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/.gitignore b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/.gitignore new file mode 100644 index 00000000..6972269b --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/modules.xml +/projectSettingsUpdater.xml +/contentModel.xml +/.idea.Scripts.iml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/encodings.xml b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/encodings.xml new file mode 100644 index 00000000..df87cf95 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/indexLayout.xml b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/indexLayout.xml new file mode 100644 index 00000000..7b08163c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/vcs.xml b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/vcs.xml new file mode 100644 index 00000000..c2365ab1 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/.idea/.idea.Scripts/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/App.config b/TSTSSESCores/Data/Scripts/Scripts/App.config new file mode 100644 index 00000000..bae5d6d8 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Action2.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Action2.cs new file mode 100644 index 00000000..bbc03219 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Action2.cs @@ -0,0 +1,9 @@ +namespace MIG.Shared.CSharp { + public interface Action2 { + void run(T t, K k); + } + + public interface Action1 { + void run(T t); + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/BlockIdmatcher.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/BlockIdmatcher.cs new file mode 100644 index 00000000..f49b0d92 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/BlockIdmatcher.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using VRage.Game; + +namespace SharedLib +{ + + public class BlockIdMatcherWithCache : BlockIdMatcher + { + private Dictionary matches = new Dictionary(); + public BlockIdMatcherWithCache(string s) : base(s) + { + } + + public override bool Matches(MyDefinitionId Id) + { + bool result; + if (!matches.TryGetValue(Id, out result)) + { + result = base.Matches(Id); + matches[Id] = result; + return result; + } + + return result; + } + } + + /// + /// Examples: */*AdminBlock* */*Admin-Block A/*Admin-Block *!Admin-Block */* a/a + /// + public class BlockIdMatcher + { + private static Regex regex = new Regex("([*\\w-_0-9]+)?(?:\\/)?([*\\w-_0-9]+)"); + public List checks = new List(); + + public BlockIdMatcher(string s) + { + if (s == null) { return; } + var m = regex.Matches(s); + for (var x = 0; x < m.Count; x++) + { + var match = m[x]; + switch (match.Groups.Count) + { + case 2: + { + var w = match.Groups[1].Value; + checks.Add(new TypeSubtypeMatcher(TypeSubtypeMatcher.MODE_ANY, String.Empty, GetMode(w), w.Replace("*", ""))); + break; + } + case 3: + { + var w1 = match.Groups[1].Value; + var w2 = match.Groups[2].Value; + checks.Add(new TypeSubtypeMatcher(GetMode(w1), w1.Replace("*", ""), GetMode(w2), w2.Replace("*", ""))); + break; + } + } + } + } + + public override string ToString() + { + var s = ""; + foreach(var x in checks) + { + s += x.ToString() + " "; + } + + return $"BlockIdMatcher:{checks.Count} [{s}]"; + } + + private int GetMode(string w) + { + var v = !w.Contains("!"); + if (w == "*") return TypeSubtypeMatcher.MODE_ANY; + if (w.StartsWith("*") && w.EndsWith("*")) return v ? TypeSubtypeMatcher.MODE_CONTAINS : TypeSubtypeMatcher.MODE_NOT_CONTAINS; + if (w.StartsWith("*")) return v ? TypeSubtypeMatcher.MODE_ENDS : TypeSubtypeMatcher.MODE_NOT_ENDS; + if (w.EndsWith("*")) return v ? TypeSubtypeMatcher.MODE_STARTS : TypeSubtypeMatcher.MODE_NOT_STARTS; + return TypeSubtypeMatcher.MODE_EXACT; + } + + public virtual bool Matches(string type, string subtype) + { + foreach (var x in checks) + { + if (x.Matches(type, subtype)) + { + return true; + } + } + return false; + } + + + public virtual bool Matches(MyDefinitionId Id) + { + return Matches(Id.TypeId.ToString().Replace("MyObjectBuilder_", ""), Id.SubtypeName); + } + + public class TypeSubtypeMatcher + { + public const int MODE_EXACT = 0; + + public const int MODE_STARTS = 1; + public const int MODE_ENDS = 2; + public const int MODE_CONTAINS = 3; + + public const int MODE_NOT_STARTS = 4; + public const int MODE_NOT_ENDS = 5; + public const int MODE_NOT_CONTAINS = 6; + + public const int MODE_ANY = 7; + + + public int modeType = 0; + public int modeSubType = 0; + public string typeString = null; + public string subtypeString = null; + + + public TypeSubtypeMatcher(int modeType, string typeString, int modeSubType, string subtypeString) + { + this.modeType = modeType; + this.typeString = typeString; + this.modeSubType = modeSubType; + this.subtypeString = subtypeString; + } + + public bool MatchesType(string type) + { + switch (modeType) + { + case MODE_EXACT: + if (type != typeString) return false; + break; + case MODE_STARTS: + if (!type.StartsWith(typeString)) return false; + break; + case MODE_NOT_STARTS: + if (type.StartsWith(typeString)) return false; + break; + case MODE_ENDS: + if (!type.EndsWith(typeString)) return false; + break; + case MODE_NOT_ENDS: + if (type.EndsWith(typeString)) return false; + break; + case MODE_CONTAINS: + if (!type.Contains(typeString)) return false; + break; + case MODE_NOT_CONTAINS: + if (type.Contains(typeString)) return false; + break; + } + + return true; + } + + public bool MatchesSubtype(string subtype) + { + switch (modeSubType) + { + case MODE_EXACT: + if (subtype != subtypeString) return false; + break; + case MODE_STARTS: + if (!subtype.StartsWith(subtypeString)) return false; + break; + case MODE_NOT_STARTS: + if (subtype.StartsWith(subtypeString)) return false; + break; + case MODE_ENDS: + if (!subtype.EndsWith(subtypeString)) return false; + break; + case MODE_NOT_ENDS: + if (subtype.EndsWith(subtypeString)) return false; + break; + case MODE_CONTAINS: + if (!subtype.Contains(subtypeString)) return false; + break; + case MODE_NOT_CONTAINS: + if (subtype.Contains(subtypeString)) return false; + break; + } + + return true; + } + + public bool Matches(string type, string subtype) + { + return MatchesType(type) && MatchesSubtype(subtype); + } + + public override string ToString() + { + return typeString + "/" + subtypeString + "[" + modeType + "/" + modeSubType + "]"; + } + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/BlueprintsHelper.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/BlueprintsHelper.cs new file mode 100644 index 00000000..fe72792f --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/BlueprintsHelper.cs @@ -0,0 +1,110 @@ +using Sandbox.Definitions; +using VRage.ObjectBuilders; + +namespace MIG.Shared.SE +{ + + public enum BlueprintType + { + OreToIngot, + OresToIngots, + OresToOres, + + IngotsToIngots, + IngotsToComponent, + IngotsToComponents, + + ComponentsToComponents, + + OtherToTools, + IngotsToTools, + + Other + } + + public static class BlueprintsHelper + { + public static readonly MyObjectBuilderType COMPONENT = MyObjectBuilderType.Parse("MyObjectBuilder_Component"); + public static readonly MyObjectBuilderType ORE = MyObjectBuilderType.Parse("MyObjectBuilder_Ore"); + public static readonly MyObjectBuilderType INGOT = MyObjectBuilderType.Parse("MyObjectBuilder_Ingot"); + public static readonly MyObjectBuilderType TOOL = MyObjectBuilderType.Parse("MyObjectBuilder_PhysicalGunObject"); + public static readonly MyObjectBuilderType TOOL2 = MyObjectBuilderType.Parse("MyObjectBuilder_OxygenContainerObject"); + + public static BlueprintType GetBlueprintType(this MyBlueprintDefinitionBase b) + { + var hasInputOres = false; + var hasInputIngots = false; + var hasInputComponents = false; + var hasInputOther = false; + + var hasOutputOres = false; + var hasOutputIngots = false; + var hasOutputComponents = false; + var hasOutputTools = false; + var hasOutputOther = false; + + foreach (var r in b.Prerequisites) + { + if (r.Id.TypeId == COMPONENT) + { + hasInputComponents = true; + continue; + } + if (r.Id.TypeId == ORE) + { + hasInputOres = true; + continue; + } + if (r.Id.TypeId == INGOT) + { + hasInputIngots = true; + continue; + } + + hasInputOther = true; + } + + foreach (var r in b.Results) + { + if (r.Id.TypeId == COMPONENT) + { + hasOutputComponents = true; + continue; + } + if (r.Id.TypeId == TOOL || r.Id.TypeId == TOOL2) + { + hasOutputTools = true; + continue; + } + if (r.Id.TypeId == ORE) + { + hasOutputOres = true; + continue; + } + if (r.Id.TypeId == INGOT) + { + hasOutputIngots = true; + continue; + } + + hasOutputOther = true; + } + + var i = (hasInputOres ? 1 : 0) + (hasInputIngots ? 1 : 0) + (hasInputComponents ? 1 : 0) + (hasInputOther ? 1 : 0); + var o = (hasOutputOres ? 1 : 0) + (hasOutputIngots ? 1 : 0) + (hasOutputComponents ? 1 : 0) + (hasOutputTools ? 1 : 0) + (hasOutputOther ? 1 : 0); + + if (i != 1) return BlueprintType.Other; + if (o != 1) return BlueprintType.Other; + + if (hasOutputTools) return hasInputIngots ? BlueprintType.IngotsToTools : BlueprintType.OtherToTools; + if (hasInputOres && hasOutputIngots) return b.Results.Length == 1 && b.Prerequisites.Length == 1 ? BlueprintType.OreToIngot : BlueprintType.OresToIngots; + if (hasInputIngots && hasOutputComponents) return b.Results.Length > 1 ? BlueprintType.IngotsToComponents : BlueprintType.IngotsToComponent; + if (hasInputOres && hasOutputOres) return BlueprintType.OresToOres; + if (hasInputIngots && hasOutputIngots) return BlueprintType.IngotsToIngots; + if (hasInputComponents && hasOutputComponents) return BlueprintType.ComponentsToComponents; + if (hasOutputTools) return BlueprintType.IngotsToTools; + + return BlueprintType.Other; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Common.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Common.cs new file mode 100644 index 00000000..e3cab113 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Common.cs @@ -0,0 +1,132 @@ +using Sandbox.Game; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Linq; +using ProtoBuf; +using VRage.Game.ModAPI; +using VRageMath; + +namespace MIG.Shared.SE { + public static class Common { + + [ProtoContract] + class PlayerMessage + { + [ProtoMember(1)] + public string message; + + [ProtoMember(2)] + public string author; + + + [ProtoMember(4)] public string font = "Blue"; + } + + private static Connection SendMessageConnection; + public static void Init() + { + SendMessageConnection = new Connection(16623, RequestSendMessageHandler); + } + + + private static void RequestSendMessageHandler(PlayerMessage arg1, ulong arg2, bool arg3) + { + SendChatMessage(arg1.message, arg1.author, arg2.IdentityId(), arg1.font); + } + + public static void SendChatMessage(string message, string author = "", long playerId = 0L, string font = "Blue") { + if (!MyAPIGateway.Session.IsServer) + { + SendMessageConnection.SendMessageToServer(new PlayerMessage() + { + message = message, + author = author, + font = font + }); + } + else + { + MyVisualScriptLogicProvider.SendChatMessage (message, author, playerId, font); + } + + } + + public static void SendChatMessageToMe(string message, string author = "", string font = "Blue") { + if (MyAPIGateway.Session.Player != null) { + MyVisualScriptLogicProvider.SendChatMessage (message, author, MyAPIGateway.Session.Player.IdentityId, font); + } + } + + public static void ShowNotification(string message, int disappearTimeMs, string font = "White", long playerId = 0L) { + MyVisualScriptLogicProvider.ShowNotification (message, disappearTimeMs, font, playerId); + } + + public static void ShowNotificationForAllInRange(string message, int disappearTimeMs, Vector3D pos, float r, string font = "White") { + var pl = GetOnlinePlayersInRange (pos, r); + foreach (var x in pl) { + MyVisualScriptLogicProvider.ShowNotification (message, disappearTimeMs, font, x.IdentityId); + } + } + + + public static void ShowNotificationForMeInGrid(IMyCubeGrid grid, string message, int disappearTimeMs, string font = "White") { + if (MyAPIGateway.Session.isTorchServer()) return; + try { + var cock = MyAPIGateway.Session.Player.Controller.ControlledEntity as IMyCockpit; + if (cock == null) return; + if (cock.CubeGrid != grid) { + return; + } + MyVisualScriptLogicProvider.ShowNotificationLocal(message, disappearTimeMs, font); + } catch (Exception e) { } + } + + + public static List GetOnlinePlayersInRange (Vector3D pos, float r) { + List players = new List(); + r = r*r; + MyAPIGateway.Multiplayer.Players.GetPlayers(players, (x)=>{ + var ch = x.Character; + if (ch != null) { + return (ch.WorldMatrix.Translation - pos).LengthSquared() < r; + } + return false; + }); + return players; + } + + + + + + public static String getPlayerName (long id) { + var p = getPlayer (id); + return p ==null ? "UnknownP" : p.DisplayName; + } + + public static IMyPlayer getPlayer (long id) { + var ind = new List(); + + MyAPIGateway.Players.GetPlayers (ind, (x) => { return x.IdentityId == id; }); + return ind.FirstOrDefault(null) as IMyPlayer; + } + //public static bool isBot (long id) { + // var ind = new List(); + // MyAPIGateway.Players.GetAllIdentites (ind, (x) => { return x.IdentityId == id; }); + // + // if (ind.Count == 1) { + // ind[0]. + // } + //} + + //public static void ShowNotificationToAll(string message, int disappearTimeMs, string font = "White") { + // MyVisualScriptLogicProvider.ShowNotificationToAll (message, disappearTimeMs, font); + //} + // + //public static void ShowSystemMessage(string from, string text, long player) { + // //MyAPIGateway.Utilities.ShowMessage("System", "Killed by : [" +killer.DisplayName + "] Sent to him: [" + (-took)+"] credits"); + // + //} + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/EconomyHelper.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/EconomyHelper.cs new file mode 100644 index 00000000..764c3fcb --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/EconomyHelper.cs @@ -0,0 +1,96 @@ +using System.Collections.Generic; +using Sandbox.Definitions; +using VRage.Game; +using VRage.Game.ModAPI; +using VRage.ObjectBuilders; + +namespace MIG.Shared.SE { + public static class EconomyHelper { + public static MyObjectBuilderType COMPONENT = MyObjectBuilderType.Parse("MyObjectBuilder_Component"); + public static MyObjectBuilderType INGOT = MyObjectBuilderType.Parse("MyObjectBuilder_Ingot"); + public static MyObjectBuilderType ORE = MyObjectBuilderType.Parse("MyObjectBuilder_Ore"); + public static MyDefinitionId ASSEMBLER_TIME = new MyDefinitionId(ORE, "AssemblerTime"); + public static MyDefinitionId REFINERY_TIME = new MyDefinitionId(ORE , "RefineryTime"); + public static MyDefinitionId WORTH = new MyDefinitionId(ORE, "Money"); + public static MyDefinitionId WORTH_OK = new MyDefinitionId(ORE, "MoneyOk"); + + + public static long changeMoney (this IMyPlayer pl, double perc, long amount = 0) { + long bal; + if (pl.TryGetBalanceInfo (out bal)) { + var fee = (long)(bal * perc + amount); + var take = fee - bal > 0? bal : fee; + pl.RequestChangeBalance (fee); + return fee; + } + return 0; + } + + public static long changeMoney (this IMyFaction pl, double perc, long amount = 0) { + long bal; + if (pl.TryGetBalanceInfo (out bal)) { + var fee = (long)(bal * perc + amount) ; + var take = fee - bal > 0? bal : fee; + pl.RequestChangeBalance (take); + return take; + } + return 0; + } + + + + + private static void FormWorth(Dictionary> dict, MyBlueprintDefinitionBase b, Dictionary mapper, bool isRef, MyBlueprintDefinitionBase.Item result) + { + var from = isRef ? b.Results: b.Results; + var to = isRef ? b.Prerequisites : b.Prerequisites; + var time = isRef ? REFINERY_TIME : ASSEMBLER_TIME; + + var key = result.Id; + var am = result.Amount; + var d = new Dictionary(); + + if (result.Id.SubtypeName.Contains("Scrap")) return; + + double w; + var money = 0.0; + var hasAllMappings = true; + + foreach (var y in to) + { + if (y.Id.SubtypeName.Contains("Scrap")) return; + w = (double)y.Amount / (double)am; + d[y.Id] = w;// * assemblerMutliplier; + + if (mapper.ContainsKey(y.Id)) + { + money += mapper[y.Id] * w; + } + else + { + hasAllMappings = false; + } + } + + w = b.BaseProductionTimeInSeconds / (double)am; + d[time] = w; + + if (hasAllMappings && mapper.ContainsKey(time)) + { + money += w * mapper[time]; + } + else + { + hasAllMappings = false; + } + + d[WORTH] = money; + if (hasAllMappings) + { + d[WORTH_OK] = 1; + } + + dict[key] = d; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Ext.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Ext.cs new file mode 100644 index 00000000..6f82b704 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Ext.cs @@ -0,0 +1,113 @@ +using Sandbox.Common.ObjectBuilders; +using Sandbox.Definitions; +using Sandbox.Game.Entities; +using System; +using System.Collections.Generic; +using MIG.SpecCores; +using VRage.Game; +using VRage.Game.ModAPI; +using VRageMath; + +namespace MIG.Shared.SE { + public static class Ext { + public static T FindAndMove (this List list, int newPos, Func x) { + var ind = list.FindIndex ((y) => x.Invoke(y)); + if (ind != -1) { + var tt = list[ind]; + list.Move (ind, newPos); + return tt; + } else { return default(T); } + } + + public static void FindAndMoveAll (this List list, int newPos, Func x) { + var ind = list.FindIndex ((y) => x.Invoke(y)); + var ind2 = list.FindLastIndex((y) => x.Invoke(y)); + + + if (ind != -1 && ind2 != -1) { + var tt = list[ind]; + + var pos = 0; + for (var i = ind; i<=ind2; i++) + { + list.Move (i, newPos+pos); + pos++; + } + } + } + + public static Ship GetShip(this IMyCubeGrid grid) { + var x = OriginalSpecCoreSession.Instance.gridToShip; + if (x.ContainsKey(grid.EntityId)) return x[grid.EntityId]; + else return null; + } + + + public static Dictionary GetBlockPrice (this IMySlimBlock slim, Dictionary dict = null) { + if (dict == null) dict = new Dictionary(); + + var cmps = (slim.BlockDefinition as MyCubeBlockDefinition).Components; + + foreach (var xx in cmps) { + var id = xx.Definition.Id; + var c = xx.Count; + if (dict.ContainsKey(id)) { + dict[id] += c; + } else { + dict.Add (id, c); + } + } + + return dict; + } + + public static Dictionary GetBlockLeftNeededComponents (this IMySlimBlock slim, Dictionary dict = null, Dictionary temp = null) { + if (dict == null) dict = new Dictionary(); + + var cmps = (slim.BlockDefinition as MyCubeBlockDefinition).Components; + + temp.Clear(); + foreach (var xx in cmps) { + var id = xx.Definition.Id; + var c = xx.Count; + if (temp.ContainsKey(id)) { + temp[id] += c; + } else { + temp.Add (id, c); + } + } + + foreach (var x in temp) { + var id = x.Key; + var has = slim.GetConstructionStockpileItemAmount (id); + var need = x.Value; + var left = need - has; + if (left > 0) { + if (dict.ContainsKey(id)) { + dict[id] += left; + } else { + dict.Add (id, left); + } + } + } + + return dict; + } + + public static Vector3D GetWorldPosition (this IMySlimBlock block) { + var box = new BoundingBoxD (); + block.GetWorldBoundingBox(out box); + return box.Center; + } + + public static bool Contains (this MySafeZone __instance, Vector3 point) { + if (__instance.Shape == MySafeZoneShape.Sphere) { + BoundingSphereD boundingSphereD = new BoundingSphereD(__instance.PositionComp.GetPosition(), (double)__instance.Radius); + return boundingSphereD.Contains(point) == ContainmentType.Contains; + } else { + MyOrientedBoundingBoxD myOrientedBoundingBoxD = new MyOrientedBoundingBoxD(__instance.PositionComp.LocalAABB, __instance.PositionComp.WorldMatrix); + return myOrientedBoundingBoxD.Contains(ref point); + } + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/FrameExecutor.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/FrameExecutor.cs new file mode 100644 index 00000000..d19ddc8c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/FrameExecutor.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.CSharp; +using VRage.ModAPI; + +namespace MIG.Shared.SE { + public static class FrameExecutor { + private static int frame = 0; + public static int currentFrame { get { return frame; } } + + private static readonly List> onEachFrameLogic = new List>(); + private static readonly List> addOnEachFrameLogic = new List>(); + private static readonly List> removeOnEachFrameLogic = new List>(); + private static bool needRemoveFrameLogic = false; + + public static void Update() { + try { + foreach (var x in onEachFrameLogic) { + x.run(frame); + } + + onEachFrameLogic.AddList(addOnEachFrameLogic); + foreach (var x in removeOnEachFrameLogic) + { + onEachFrameLogic.Remove(x); + } + addOnEachFrameLogic.Clear(); + removeOnEachFrameLogic.Clear(); + frame++; + } catch (Exception e) { + Log.ChatError("FrameExecutor", e); + } + } + + public static void addFrameLogic(Action1 action) { + addOnEachFrameLogic.Add(action); + } + + public static ActionWrapper addFrameLogic(Action action) { + ActionWrapper wrapper = new ActionWrapper(action); + addOnEachFrameLogic.Add(wrapper); + return wrapper; + } + + public static BlockWrapperAction addFrameLogic(Timer timer, IMyEntity entity, Action action) + { + return addFrameLogic(timer, entity, new ActionWrapper(action)); + } + + public static BlockWrapperAction addFrameLogic(Timer timer, IMyEntity entity, Action1 action) + { + if (entity == null) + { + Log.ChatError("addFrameLogic:Entity is null"); + return new BlockWrapperAction(entity, timer, action); + } + BlockWrapperAction wrapper = new BlockWrapperAction(entity, timer, action); + addOnEachFrameLogic.Add(wrapper); + return wrapper; + } + + public static void removeFrameLogic(Action1 action) { + removeOnEachFrameLogic.Add(action); + } + + public static void addDelayedLogic(long frames, Action1 action) { + addOnEachFrameLogic.Add(new DelayerAction(frames, action)); + } + + public static DelayerAction addDelayedLogic(long frames, Action action) + { + var da = new DelayerAction(frames, new ActionWrapper(action)); + addOnEachFrameLogic.Add(da); + return da; + } + + public class ActionWrapper : Action1 + { + Action action; + public ActionWrapper (Action action) + { + this.action = action; + } + + public void run(long t) + { + action(t); + } + + public void Unsub() + { + FrameExecutor.removeFrameLogic(this); + } + } + + + public class BlockWrapperAction : Action1 { + private Timer timer; + private Action1 action; + private IMyEntity entity; + public BlockWrapperAction(IMyEntity entity, Timer timer, Action1 action) { + this.timer = timer; + this.action = action; + this.entity = entity; + if (entity == null) + { + + } + } + + public void Cancel() + { + FrameExecutor.removeFrameLogic(this); + } + + public void RunNow() + { + action.run(-1); + } + + public void run(long k) { + if (timer.tick()) + { + if (entity.MarkedForClose || entity.Closed) + { + Cancel(); + return; + } + action.run(k); + } + } + } + + + public class DelayerAction : Action1 { + private long timer; + private Action1 action; + public DelayerAction(long timer, Action1 action) { + this.timer = timer; + this.action = action; + } + + public void Cancel() + { + FrameExecutor.removeFrameLogic(this); + } + + public void RunNow() + { + action.run(-1); + } + + public void run(long k) { + if (timer > 0) { + timer--; return; + } + FrameExecutor.removeFrameLogic(this); + action.run(k); + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/FreezableTimerLogic.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/FreezableTimerLogic.cs new file mode 100644 index 00000000..af57453e --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/FreezableTimerLogic.cs @@ -0,0 +1,73 @@ +using System; +using MIG.Shared.CSharp; +using ProtoBuf; +using VRage.ModAPI; + +namespace MIG.Shared.SE +{ + public class FreezableTimerLogic + { + private Wrapper SettingsWrapper; + private Action TickAction; + private IMyEntity Entity; + private Guid Guid; + private long MinimalMsToTrigger; + + private long LastRun + { + get { return SettingsWrapper.LastRun; } + set { SettingsWrapper.LastRun = value; } + } + + //public T Settings { + // get { return SettingsWrapper.Settings; } + // set { SettingsWrapper.Settings = value; } + //} + + public void Init(Guid guid, IMyEntity entity, Action tick, Func getDefaultSettings, long minimalMsToTrigger = 1000, bool stopAtStart = false) + { + Guid = guid; + Entity = entity; + TickAction = tick; + MinimalMsToTrigger = minimalMsToTrigger; + if (!Entity.TryGetStorageData(guid, out SettingsWrapper, protoBuf:true)) + { + SettingsWrapper = new Wrapper(); + //SettingsWrapper.Settings = getDefaultSettings(); + LastRun = stopAtStart ? -1 : SharpUtils.msTimeStamp(); + } + + } + + public void Tick() + { + if (LastRun < 0) + { + return; + } + var dx = (SharpUtils.msTimeStamp() - LastRun) / MinimalMsToTrigger; + if (dx < 0) return; + TickAction((int) dx); + LastRun += MinimalMsToTrigger * dx; + Save(); + } + + public void Stop() { if (LastRun != -1) { LastRun = -1; Save(); } } + + public void Start(int extraTime) { LastRun = SharpUtils.msTimeStamp() - extraTime; Save(); } + + public void AddTime(int time) { LastRun -= time; Save(); } + + private void Save() { Entity.SetStorageData(Guid, SettingsWrapper, protoBuf:true); } + + [ProtoContract] + private class Wrapper + { + [ProtoMember(1)] + public long LastRun; + + [ProtoMember(2)] + public T Settings; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/GameLogicWithSyncAndSettings.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/GameLogicWithSyncAndSettings.cs new file mode 100644 index 00000000..c9b21902 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/GameLogicWithSyncAndSettings.cs @@ -0,0 +1,286 @@ +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using VRage.Game.Components; +using VRage.ObjectBuilders; +using VRage.ModAPI; +using Sandbox.Game.EntityComponents; +using Digi; + +namespace MIG.Shared.SE +{ + /*[ProtoContract] + public class TestSettings + { + [ProtoMember(1)] + public float CurrentThrust; + } + + public class TestBlockSettings + { + public float FlameLength; + public float MaxThrust; + } + + + public class TestGameLogic : GameLogicWithSyncAndSettings + { + private static Guid GUID = new Guid(); + private static Sync sync; + + public override TestSettings GetDefaultSettings() { return new TestSettings { CurrentThrust = 0f }; } + public override Guid GetGuid() { return GUID; } + public override Sync GetSync() { return sync; } + public override TestBlockSettings InitBlockSettings() { + return new TestBlockSettings() { FlameLength = 5f }; + } + + public static void Init () + { + sync = new Sync(53334, (x)=>x.Settings, Handler); + } + + protected override void OnSettingsChanged() + { + + } + + public override void ApplyDataFromClient(TestSettings arrivedSettings) + { + Settings.CurrentThrust = MathHelper.Clamp(arrivedSettings.CurrentThrust, 0, BlockSettings.MaxThrust); + } + }*/ + + public abstract class GameLogicWithSyncAndSettings : MyGameLogicComponent where FinalClass : GameLogicWithSyncAndSettings + { + /// + /// Get guid, that belongs to this type of gamelogic. Must be STATIC and UNIQ per each nested class + /// + /// + protected abstract Guid GetGuid(); + + /// + /// Get sync, that belongs to this type of gamelogic. Must be STATIC and UNIQ per each nested class + /// + /// + protected abstract Sync GetSync(); + + /// + /// Called, when data arrives on server from clients. + /// You must apply changes to gameLogic.Settings + /// + /// Data that arrived from client + protected abstract void ApplyDataFromClient (DynamicSettings arrivedSettings,ulong userSteamId, byte type); + + + /// + /// If new block placed, what settings it will have? + /// + /// + protected abstract DynamicSettings GetDefaultSettings(); + + /// + /// When block placed, we should define here static setting. + /// + /// + protected abstract StaticSettings InitBlockSettings(); + + /// + /// Data that is automaticly transfered between client and server. It is also stored in settings. + /// + public DynamicSettings Settings; + + /// + /// Data that is not changed at all. It is somthing like SBC values + /// + public StaticSettings BlockSettings; + + /// + /// Called when settings were changed + /// + protected virtual void OnSettingsChanged() { } + + /// + /// Called when settings are loaded + /// + protected virtual void OnInitedSettings() { } + + /// + /// Called once per blockClass. Here you init controls for your block gui + /// + protected virtual void InitControls() { } + + /// + /// Called once in first UpdateBeforeFrame + /// + protected virtual void OnceInitBeforeFrame () { } + + private static readonly HashSet InitedControls = new HashSet(); + + IMyEntity myEntity; + + public override void Init(MyObjectBuilder_EntityBase objectBuilder) + { + base.Init(objectBuilder); + LoadSettings(); + BlockSettings = InitBlockSettings(); + + if (!MyAPIGateway.Session.IsServer) + { + GetSync().RequestData(Entity.EntityId); + } + + OnInitedSettings(); + + //Init controls once; + bool needInit = false; + lock (InitedControls) + { + needInit = InitedControls.Add(GetType()); + } + + if (needInit) + { + InitControls(); + } + } + + public override MyObjectBuilder_EntityBase GetObjectBuilder(bool copy = false) + { + SaveSettings(true); + return base.GetObjectBuilder(copy); + } + + + public static void Handler (FinalClass block, DynamicSettings settings, byte type,ulong userSteamId, bool isFromServer) + { + var tt = (GameLogicWithSyncAndSettings)block; + + if (isFromServer && !MyAPIGateway.Session.IsServer) + { + tt.Settings = settings; + tt.OnSettingsChanged(); + } + else + { + tt.ApplyDataFromClient(settings, userSteamId, type); + tt.NotifyAndSave(); + tt.OnSettingsChanged(); + } + } + + #region Init Settings + + /// + /// Must be called on client side, in Gui elements, or on Server side where data from client is arrived; + /// + protected void NotifyAndSave(byte type=255, bool forceSave = false) + { + try + { + if (MyAPIGateway.Session.IsServer) + { + //Log.ChatError($"NotifyFromServer:[{type}][{Settings}]"); + GetSync().SendMessageToOthers(Entity.EntityId, Settings, type: type); + SaveSettings(forceSave); + + if (!MyAPIGateway.Session.isTorchServer()) + { + ApplyDataFromClient(Settings, MyAPIGateway.Session.LocalHumanPlayer.SteamUserId, type); + OnSettingsChanged(); + } + } + else + { + var sync = GetSync(); + if (sync != null) + { + //Log.ChatError($"NotifyFromClient:[{type}][{Settings}]"); + sync.SendMessageToServer(Entity.EntityId, Settings, type: type); + } + } + } + catch (Exception ex) + { + Log.ChatError($"NotifyAndSave {type} Exception {ex} {ex.StackTrace}"); + } + } + + /// + /// Must be called on client side, in Gui elements, or on Server side where data from client is arrived; + /// + protected void Notify(DynamicSettings data, byte type = 255) + { + try + { + var sync = GetSync(); + if (sync != null) + { + //Log.ChatError($"NotifyFromClient:[{type}][{data}]"); + sync.SendMessageToServer(Entity.EntityId, data, type: type); + } + } + catch (Exception ex) + { + Log.ChatError("NotifyAndSave Exception " + ex.ToString() + ex.StackTrace); + } + } + + public override void OnAddedToContainer() + { + base.OnAddedToContainer(); + if (Entity.Storage == null) + { + Entity.Storage = new MyModStorageComponent(); + } + } + + private void LoadSettings() + { + if (!Entity.TryGetStorageData(GetGuid(), out Settings)) + { + Settings = GetDefaultSettings(); + SaveSettings(); + } + } + + private bool m_settingsDirty; + protected void SaveSettings(bool forceSave = false) + { + m_settingsDirty = true; + forceSave = true; //TODO: currently GetObjectBuilder is not called + if (MyAPIGateway.Session.IsServer) + { + if (forceSave) + { + if (m_settingsDirty) + { + Entity.SetStorageData(GetGuid(), Settings); + m_settingsDirty = false; + } + } + else + { + m_settingsDirty = true; + } + } + } + + + + #endregion + + + + private bool m_firstUpdate = true; + public override void UpdateOnceBeforeFrame() + { + base.UpdateOnceBeforeFrame(); + + if (m_firstUpdate) { + m_firstUpdate = false; + OnceInitBeforeFrame(); + } + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Getter.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Getter.cs new file mode 100644 index 00000000..a12cc257 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Getter.cs @@ -0,0 +1,14 @@ +namespace MIG.Shared.SE +{ + public interface Getter { + T Get(); + } + + public class FixedGetter : Getter { + T t; + public FixedGetter(T t) { + this.t = t; + } + public T Get() { return t; } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Gps.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Gps.cs new file mode 100644 index 00000000..5f274db4 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Gps.cs @@ -0,0 +1,68 @@ +using Digi; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using VRage.Game.ModAPI; +using VRageMath; + +namespace MIG.Shared.CSharp { + public static class Gps { + public static void AddGps(string name, string description, Vector3D position) { + IMyGps gps = MyAPIGateway.Session.GPS.Create(name, description, position, true, true); + MyAPIGateway.Session.GPS.AddLocalGps(gps); + } + + public static void AddGpsColored(string name, string description, Vector3D position, Vector3D color) + { + IMyGps gps = MyAPIGateway.Session.GPS.Create(name, description, position, true, true); + gps.GPSColor = new Color(color); + MyAPIGateway.Session.GPS.AddLocalGps(gps); + } + + public static void RemoveWithDescription (string startsWith) { + if (MyAPIGateway.Session == null) return; + if (MyAPIGateway.Session.Player == null) return; + + try { + List list = MyAPIGateway.Session.GPS.GetGpsList(MyAPIGateway.Session.Player.PlayerID); + foreach (var item in list) { + if (item.Description != null && item.Description.StartsWith(startsWith)) { + MyAPIGateway.Session.GPS.RemoveLocalGps(item); + } + } + } catch (Exception ex) { + Log.Error(ex, "RemoveSavedGPS()"); + } + } + + public static IMyGps RemoveWithDescription(string startsWith, long player, bool all = true) + { + IMyGps gps = null; + List list = MyAPIGateway.Session.GPS.GetGpsList(player); + foreach (var item in list) + { + if (item.Description != null && item.Description.StartsWith(startsWith)) + { + gps = item; + MyAPIGateway.Session.GPS.RemoveGps(player, item); + if (!all) return gps; + } + } + + return gps; + } + + public static IMyGps GetWithDescription(string startsWith, long player) + { + List list = MyAPIGateway.Session.GPS.GetGpsList(player); + foreach (var item in list) + { + if (item.Description != null && item.Description.StartsWith(startsWith)) + { + return item; + } + } + return null; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/GridAndBlocks.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/GridAndBlocks.cs new file mode 100644 index 00000000..61f070cd --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/GridAndBlocks.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; +using Sandbox.Definitions; + +namespace MIG.Shared.SE { + static class GridAndBlocks { + public static void GetRefinerySpeedAndYield(this IMyRefinery rf, out double speed, out double yield, out double power, out double ingotsPerSec) { + var def = (rf.SlimBlock.BlockDefinition as MyRefineryDefinition); + speed = (1+rf.UpgradeValues["Productivity"]) * def.RefineSpeed; + yield = rf.UpgradeValues["Effectiveness"] * def.MaterialEfficiency; + power = rf.UpgradeValues["PowerEfficiency"]; + ingotsPerSec = speed * yield; + } + + public static void GetAssemblerSpeedAndPower(this IMyAssembler rf, out double speed, out double power) { + var def = (rf.SlimBlock.BlockDefinition as MyAssemblerDefinition); + speed = (1+rf.UpgradeValues["Productivity"]) * def.AssemblySpeed; + power = rf.UpgradeValues["PowerEfficiency"]; + } + + + + public static bool checkSubtypeName(this IMySlimBlock x, string[] startsWith = null, string[] exactNames = null, bool onlyFunctional = false) { + var bd = x.BlockDefinition; + if (bd == null || bd.Id == null || bd.Id.SubtypeName == null) return false; + + var t = bd.Id.SubtypeName; + + if (onlyFunctional) { + if (!x.FatBlock.IsFunctional) { + return false; + } + } + + if (startsWith != null) { + foreach (var y in startsWith) { + if (t.StartsWith(y)) return true; + } + } + if (exactNames != null) { + foreach (var z in exactNames) { + if (z.Equals(t)) return true; + } + } + return false; + } + + public static List GetBlocksBySubtypeName(this IMyCubeGrid g, List blocks, string[] startsWith = null, string[] exactNames = null, bool onlyFunctional = false, bool inAllGrids = false) { + if (blocks == null) blocks = new List(); + + g.GetBlocks(blocks, x => checkSubtypeName(x, startsWith, exactNames, onlyFunctional)); + + if (inAllGrids) { + var connectedGrids = MyAPIGateway.GridGroups.GetGroup(g, GridLinkTypeEnum.Physical); + foreach (var y in connectedGrids) { + y.GetBlocks(blocks, x => checkSubtypeName(x, startsWith, exactNames, onlyFunctional)); + } + } + + return blocks; + } + + + public static T FindBlock(this IMyCubeGrid y) { + var blocks = new List(); + y.GetBlocks(blocks); + + foreach (var x in blocks) { + if (x.FatBlock == null) { + //Log.Info("Find Block failed:" + x.GetType().Name + " " + x); + continue; + } + if (x.FatBlock is T) { + return (T)(x.FatBlock); + } else { + //Log.Info("Find Block failed:" + x.GetType().Name + " " + x.FatBlock.GetType().Name); + } + } + + return default(T); + } + + + public static ICollection FindBlocks(this IMyCubeGrid y, ICollection ret, Func filter) { + var blocks = new List(); + y.GetBlocks(blocks); + foreach (var x in blocks) { + if (filter(x)) ret.Add((T)x.FatBlock); + } + + return ret; + } + + public static void FindBlocks (this IMyCubeGrid y, Action filter) { + var blocks = new List(); + y.GetBlocks(blocks); + foreach (var x in blocks) { + filter.Invoke(x); + } + } + + public static ICollection FindBlocks(this IMyCubeGrid y, ICollection ret) { + return y.FindBlocks(ret, x => { + var fat = x.FatBlock; + if (fat == null) return false; + return fat is T; + }); + } + + public static List FindBlocks(this IMyCubeGrid y, Func filter) { + var blocks = new List(); + var ret = new List(); + y.GetBlocks(blocks); + foreach (var x in blocks) { + if (filter(x)) ret.Add(x); + } + + return ret; + } + + public static List FindBlocksOfType(this IMyCubeGrid y) where T : IMyCubeBlock + { + var ret = new List(); + var blocks = new List(); + y.GetBlocks(blocks); + + foreach (var x in blocks) + { + var fat = x.FatBlock; + if (fat == null) continue; + if (!(fat is T)) continue; + + ret.Add((T)fat); + } + + return ret; + } + + + public static T FindBlock(this IMyCubeGrid y, string name) where T : IMyTerminalBlock { + var blocks = new List(); + y.GetBlocks(blocks); + + foreach (var x in blocks) { + var fat = x.FatBlock; + if (fat == null) continue; + if (!(fat is T)) continue; + var f = (T)fat; + if (f.CustomName.Equals(name)) return (T)(x.FatBlock); + } + + return default(T); + } + + public static IMySlimBlock FindBlock(this IMyCubeGrid y, Func filter) { + var blocks = new List(); + y.GetBlocks(blocks); + foreach (var x in blocks) { + if (filter(x)) return x; + } + + return null; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiControlDuplicateRemover.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiControlDuplicateRemover.cs new file mode 100644 index 00000000..353005d6 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiControlDuplicateRemover.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using Sandbox.ModAPI; +using Sandbox.ModAPI.Interfaces.Terminal; + +namespace Scripts.Shared +{ + public static class GuiControlDuplicateRemover + { + private static string[] duplicates; + public static void Init(params string[] dups) + { + duplicates = dups; + MyAPIGateway.TerminalControls.CustomControlGetter += TerminalControlsOnCustomControlGetter; + } + + private static void TerminalControlsOnCustomControlGetter(IMyTerminalBlock block, List controls) + { + HashSet exists = new HashSet(); + for (var index = 0; index < controls.Count; index++) + { + var action = controls[index]; + if (exists.Contains(action.Id)) + { + controls.RemoveAt(index); + index--; + continue; + } + + foreach (var dup in duplicates) + { + if (action.Id.Contains(dup)) + { + exists.Add(action.Id); + } + } + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiHelper.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiHelper.cs new file mode 100644 index 00000000..536e9c78 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiHelper.cs @@ -0,0 +1,251 @@ +using Sandbox.ModAPI; +using Sandbox.ModAPI.Interfaces.Terminal; +using System; +using System.Collections.Generic; +using System.Text; +using MIG.Shared.CSharp; +using VRage.Utils; +using VRageMath; + +namespace MIG.Shared.SE +{ + public static class GuiHelper + { + + public static long DoubleClickTimer = 0; + + public static void DoubleClick(ref long timer, Action action) + { + var now = SharpUtils.msTimeStamp(); + if (now - timer > 500) + { + timer = now; + } + else + { + action(); + DoubleClickTimer += 1000; + } + } + + public static void DoubleClick(this IMyTerminalControlButton button) + { + var action = button.Action; + button.Action = (xx) => + { + DoubleClick(ref DoubleClickTimer, () => action(xx)); + }; + } + + public static IMyTerminalControlSlider CreateSlider (this IMyTerminalControls system, string id, string name, string tooltip, float min, float max, + Func getter, Action writer, Action setter, Func getterT, Func enabled= null, Func visible = null, bool update = false) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + XControl.SetLimits(min, max); + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + + + XControl.Getter = (b) => getter(getterT (b)); + XControl.Writer = (b, t) => writer(getterT (b), t); + XControl.Setter = (b, v) => + { + setter(getterT (b), v); + if (update) XControl.UpdateVisual(); + }; + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + XControl.Writer = XControl.Writer.TryCatch(name + " Writer"); + + system.AddControl(XControl); + + return XControl; + } + + public static IMyTerminalControlLabel CreateLabel(this IMyTerminalControls system, string id, MyStringId text) + { + var control = system.CreateControl(typeof(T).Name+ "_" + id); + control.Label = text; + MyAPIGateway.TerminalControls.AddControl(control); + return control; + } + + + + public static IMyTerminalControlButton CreateButton(this IMyTerminalControls system, string id, string name, string tooltip, Action action, Func getterT, Func enabled = null, Func visible = null) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + XControl.Action = (b) => action(getterT (b)); + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Action = XControl.Action.TryCatch(name + " Action"); + + system.AddControl(XControl); + return XControl; + } + + public static IMyTerminalControlColor CreateColorPicker(this IMyTerminalControls system, string id, string name, string tooltip, Func getter, Action setter, Func getterT, Func enabled = null, Func visible = null) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + XControl.Getter = (b) => getter(getterT (b)); + XControl.Setter = (b, v) => setter(getterT (b), v); + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + + system.AddControl(XControl); + + return XControl; + } + + public static IMyTerminalControlCombobox CreateCombobox(this IMyTerminalControls system, string id, string name, string tooltip, Func getter, Action setter, List texts, Func getterT, Func enabled = null, Func visible=null, bool update = false) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + + XControl.Getter = (b) => getter(getterT (b)); + XControl.Setter = (b, v) => + { + setter(getterT (b), v); + if(update) XControl.UpdateVisual(); + }; + + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + + + XControl.ComboBoxContent = (b) => + { + var c = 0; + foreach (var x in texts) { + var i = new VRage.ModAPI.MyTerminalControlComboBoxItem(); + i.Key = c; + i.Value = x; + c++; + b.Add(i); + } + }; + XControl.SupportsMultipleBlocks = true; + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + XControl.ComboBoxContent = XControl.ComboBoxContent.TryCatch(name+ " ComboBoxContent"); + + system.AddControl(XControl); + + return XControl; + } + + public static IMyTerminalControlCheckbox CreateCheckbox(this IMyTerminalControls system, string id, string name, string tooltip, + Func getter, Action setter, Func getterT = null, Func enabled = null, Func visible = null, bool update = false) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + XControl.Getter = (b) => getter(getterT (b)); + XControl.Setter = (b, v) => + { + setter(getterT (b), v); + if(update) XControl.UpdateVisual(); + }; + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + system.AddControl(XControl); + return XControl; + } + + public static IMyTerminalControlOnOffSwitch CreateOnOff(this IMyTerminalControls system, string id, string name, string tooltip, + Func getter, Action setter, Func getterT = null, Func enabled = null, Func visible = null, bool update = false) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible (visible, getterT); + XControl.Getter = (b) => getter(getterT (b)); + XControl.Setter = (b, v) => + { + setter(getterT (b), v); + if(update) XControl.UpdateVisual(); + }; + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + system.AddControl(XControl); + return XControl; + } + + + public static IMyTerminalControlTextbox CreateTextbox(this IMyTerminalControls system, string id, string name, string tooltip, + Func getter, Action setter, Func getterT = null, Func enabled = null, Func visible = null, bool update = false) + { + var XControl = system.CreateControl(typeof(T).Name+ "_" + id); + XControl.Title = MyStringId.GetOrCompute(name); + XControl.Tooltip = MyStringId.GetOrCompute(tooltip); + XControl.Enabled = IsEnabled(enabled, getterT); + XControl.Visible = IsVisible(visible, getterT); + XControl.Getter = (b) => getter(getterT (b)); + XControl.Setter = (b, v) => + { + setter(getterT (b), v); + if (update) XControl.UpdateVisual(); + }; + + XControl.Enabled = XControl.Enabled.TryCatch(name + " Enabled"); + XControl.Visible = XControl.Visible.TryCatch(name + " Visible"); + XControl.Getter = XControl.Getter.TryCatch(name + " Getter"); + XControl.Setter = XControl.Setter.TryCatch(name + " Setter"); + + system.AddControl(XControl); + return XControl; + } + + private static Func IsEnabled (Func enabled, Func getterT) + { + return (b) => + { + var bb = getterT(b); + if (bb == null) return false; + return enabled?.Invoke(bb) ?? true; + }; + } + + private static Func IsVisible (Func isVisible, Func getterT) + { + return (b) => + { + var bb = getterT(b); + if (bb == null) return false; + return isVisible?.Invoke(bb) ?? true; + }; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiIniter.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiIniter.cs new file mode 100644 index 00000000..0b847887 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/GuiIniter.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public abstract class GUIIniter + { + private readonly HashSet initedGUI = new HashSet(); + private readonly HashSet initedInterfacesGUI = new HashSet(); + private List> possibleBlocksToInit = new List>(); + + private bool iMyTerminalWasInited = false; + protected abstract void InitControls() where T : IMyCubeBlock; + + public void CreateGui(IMyCubeBlock block) + { + if (block == null) return; + var type = block.GetType(); + if (initedGUI.Contains(type)) return; + + lock (this) + { + if (initedGUI.Contains(type)) return; + initedGUI.Add(type); + for (int i = 0; i < possibleBlocksToInit.Count; i++) + { + var fx = possibleBlocksToInit[i]; + var added = fx(block); + if (added) return; + } + + if (!iMyTerminalWasInited) + { + iMyTerminalWasInited = true; + InitControls(); + } + } + } + + + public void AddType() where T: IMyCubeBlock + { + possibleBlocksToInit.Add(Init); + } + + private bool Init(IMyCubeBlock entity) where T : IMyCubeBlock where Z : IMyCubeBlock + { + if (entity is T) + { + var added = initedInterfacesGUI.Add(typeof(T)); + if (added) + { + InitControls(); + } + + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/InventoryUtils.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/InventoryUtils.cs new file mode 100644 index 00000000..16bc8f6f --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/InventoryUtils.cs @@ -0,0 +1,416 @@ +using System; +using System.Collections.Generic; +using Sandbox.Definitions; +using VRage.Game; +using VRage; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using Sandbox.Game; +using VRageMath; +using VRage.Game.Entity; +using Sandbox.Game.Entities; +using VRage.Game.ModAPI; +using static VRage.Game.ModAPI.Ingame.MyInventoryItemExtension; + +//using VRage.Game.ModAPI.Ingame; + +namespace ServerMod { + static class InventoryUtils { + + // получает все предметы в инвентарях + public static void GetInventoryItems(IMyCubeBlock block, Dictionary dictionary, string type = "", string subtypeId = "", bool NamesByClient = false, bool IgnoreGarage = false) { + if (block == null || !block.HasInventory) return; + if (IgnoreGarage && block.SubtypeName().Contains("Garage")) return; + + for (int i = 0; i < block.InventoryCount; i++) + { + var inventory = (MyInventory) block.GetInventory(i); + var items = inventory.GetItems(); + + foreach (var item in items) + { + var _item = MyDefinitionManager.Static.TryGetPhysicalItemDefinition(item.GetDefinitionId()); + + if ((string.IsNullOrWhiteSpace(type) || _item.Id.TypeId.ToString() == type) && + _item.Id.SubtypeName.Contains(subtypeId)) + { + double count = item.Amount.RawValue / 1000000d; + + if (dictionary.ContainsKey(_item)) dictionary[_item] += count; + else dictionary.Add(_item, count); + } + } + } + } + + public static void GetInventoryItems(IMyCubeBlock block, Dictionary dictionary, string type = "", string subtypeId = "", bool IgnoreGarage = false) { + if (block == null || !block.HasInventory) return; + if (IgnoreGarage && block.SubtypeName().Contains("Garage")) return; + + for (int i = 0; i < block.InventoryCount; i++) + { + var inventory = (MyInventory) block.GetInventory(i); + var items = inventory.GetItems(); + + foreach (var item in items) + { + var _item = MyDefinitionManager.Static.TryGetPhysicalItemDefinition(item.GetDefinitionId()); + + if ((string.IsNullOrWhiteSpace(type) || _item.Id.TypeId.ToString() == type) && + _item.Id.SubtypeName.Contains(subtypeId)) + { + double count = item.Amount.RawValue / 1000000d; + + if (dictionary.ContainsKey(_item)) dictionary[_item] += count; + else dictionary.Add(_item, count); + } + } + } + } + + // получает текущее компоненты в блоках + public static void GetComponents(this IMySlimBlock block, IDictionary dictionary) { + var components = (block.BlockDefinition as MyCubeBlockDefinition).Components; + + foreach (var component in components) + { + var name = component.DeconstructItem; + int count = component.Count; + + if (dictionary.ContainsKey(name)) dictionary[name] += count; + else dictionary.Add(name, count); + } + + var missingComponents = new Dictionary(); + block.GetMissingComponentsItemsDefinitions(missingComponents); + + foreach (var component in missingComponents) + { + var item = component.Key; + var count = component.Value; + + if (dictionary.ContainsKey(item)) dictionary[item] -= count; + else dictionary.Add(item, count); + } + } + public static void GetMissingComponentsItemsDefinitions(this IMySlimBlock block, IDictionary dictionary) { + + var missingComponents = new Dictionary(); + block.GetMissingComponents(missingComponents); + + foreach (var component in missingComponents) { + var item = MyDefinitionManager.Static.TryGetPhysicalItemDefinition(MyDefinitionId.Parse("MyObjectBuilder_Component/" + component.Key)); + int count = component.Value; + + if (dictionary.ContainsKey(item)) dictionary[item] -= count; + else dictionary.Add(item, count); + } + } + + // получает все компоненты в блоках + public static void GetTotalComponents(this IMySlimBlock block, Dictionary dictionary) { + var components = (block.BlockDefinition as MyCubeBlockDefinition).Components; + + foreach (var component in components) + { + var name = component.DeconstructItem; + + int count = component.Count; + + if (dictionary.ContainsKey(name)) dictionary[name] += count; + else dictionary.Add(name, count); + } + } + + public static void GetComponentsTranslation(this IMySlimBlock block, Dictionary dictionary) + { + var components = (block.BlockDefinition as MyCubeBlockDefinition).Components; + + foreach (var component in components) + { + var SubtypeName = component.Definition.Id.SubtypeName; + var TextName = component.Definition.DisplayNameText; + + if (!dictionary.ContainsKey(SubtypeName)) dictionary.Add(SubtypeName, TextName); + } + + } + + + // получает объем компонентов в блоках + public static float GetComponentsVolume(this IMySlimBlock block) + + { + float total = 0f; + foreach (var component in (block.BlockDefinition as MyCubeBlockDefinition).Components) + { + total += component.Definition.Volume; + } + return total; + } + + + + // получает недостающие компоненты + // IMySlimBlock.GetConstructionStockpileItemAmount() работает (работал) некорректно, + // поэтому поиск через IMySlimBlock.GetMissingComponents() + public static void GetMissingComponents(this IMySlimBlock block, Dictionary dictionary, bool NamesByClient = false) + { + var missingComponents = new Dictionary(); + block.GetMissingComponents(missingComponents); + + foreach (var component in missingComponents) { + string name = component.Key; + int count = component.Value; + + if (dictionary.ContainsKey(name)) dictionary[name] += count; + else dictionary.Add(name, count); + } + } + + + + public static void GetAllCargosInRange (ref BoundingSphereD sphere2, List cargos, string blockName, bool allowAssemblers = false, Func filter = null, Func sort = null, int maxTake = Int32.MaxValue) { + var data = MyEntities.GetTopMostEntitiesInSphere(ref sphere2); + var ships = new HashSet(); + foreach (var x in data) { + var g = x as MyCubeGrid; + if (g != null) { + ships.Add(g); + } + } + data.Clear(); + + var allBlocks = new List(); + var enters = new List(); + var sphere3 = sphere2; + + foreach (var x in ships) { + x?.OverFatBlocks((b) => { + if (!(b is IMyCargoContainer) && !(b is IMyShipConnector) && !(allowAssemblers && b is IMyAssembler)) return; + var term = b as IMyTerminalBlock; + if (!term.IsFunctional) return; + if (term.CustomName.Contains(blockName)) return; + if (!term.HasLocalPlayerAccess()) return; + if (sphere3.Contains(term.WorldMatrix.Translation) == ContainmentType.Contains && !IsSeparated(term)) { + var add = true; + foreach (var e in enters) { + if (AreConnected(e, term)) { + add = false; + break; + } + } + if (add) enters.Add(term); + } + + if (filter == null || filter.Invoke(term)) { + allBlocks.Add(term); + } + }); + } + + if (sort != null) { + allBlocks.Sort((a,b)=>sort.Invoke(a, b)); + } + + foreach (var x in allBlocks) { + if (IsSeparated(x)) { + cargos.Add (x as MyEntity); + if (cargos.Count >= maxTake) return; + } else { + foreach (var y in enters) { + if (AreConnected (x, y)) { + cargos.Add (x as MyEntity); + if (cargos.Count >= maxTake) return; + break; + } + } + } + } + + } + + public static void GetAllCargosInRangeSimple(ref BoundingSphereD sphere2, List cargos, Func filter = null) + { + var data = MyEntities.GetTopMostEntitiesInSphere(ref sphere2); + var ships = new HashSet(); + foreach (var x in data) + { + var g = x as MyCubeGrid; + if (g != null) + { + ships.Add(g); + } + } + data.Clear(); + + var allBlocks = new List(); + var sphere3 = sphere2; + + foreach (var x in ships){ + x?.OverFatBlocks((b) => { + if (!(b is IMyCargoContainer) && !(b is IMyShipConnector)) return; + var term = b as IMyTerminalBlock; + if (!term.IsFunctional) return; + if (!term.HasLocalPlayerAccess()) return; + if (sphere3.Contains(term.WorldMatrix.Translation) != ContainmentType.Contains) return; + + if (filter == null || filter.Invoke(term)) { + cargos.Add(b); + } + }); + } + + } + + private static bool IsSeparated(IMyTerminalBlock a) { + var sn = a.SlimBlock.BlockDefinition.Id.SubtypeName; + if (sn.StartsWith("Freight")) return true; + if (sn.Equals("LargeBlockLockerRoom") || sn.Equals("LargeBlockLockerRoomCorner") || sn.Equals("LargeBlockLockers")) return true; + return false; + } + + private static bool AreConnected (IMyTerminalBlock a, IMyTerminalBlock b) { + //if (a.CubeGrid != b.CubeGrid) return false; + if (a==b) return true; + + for (var x=0; x < a.InventoryCount; x++) { + for (var y=0; y < b.InventoryCount; y++) { + if (a.GetInventory (x).IsConnectedTo (b.GetInventory(y))) { + return true; + } + } + } + + return false; + } + + + public static string CustomName (this IMyInventory x) { + var term = (x.Owner as IMyTerminalBlock); + if (term == null) return x.Owner.DisplayName; + else return term.CustomName; + } + + public static MyFixedPoint calculateCargoMass (this IMyCubeGrid grid) { + MyFixedPoint mass = 0; + grid.FindBlocks(x => { + + var fat = x.FatBlock; + if (fat == null || !fat.HasInventory) return false; + var i = fat.GetInventory(); + if (i == null) return false; + + mass += i.CurrentMass; + return false; + }); + + return mass; + } + + + + + public static int GetItemIndexByID (this IMyInventory inv, uint itemId) { + for (var index=0; index p = null, bool alwaysAtEnd = false) { + for (var x=from.ItemCount-1; x>=0; x--) { + var t = from.GetItemAt(x); + MyFixedPoint? amount = p!= null ? p.Invoke(t.Value) : null; + if (amount == null || amount > 0) { + from.TransferItemTo (inventory, x, checkConnection:false, amount:amount, targetItemIndex: (alwaysAtEnd ? (int?)inventory.ItemCount : null)); + } + } + } + + + public static void PullRequest (this IMyInventory target, List from, Dictionary what, bool alwaysAtEnd = false) { + foreach (var x in from) { + if (what.Count == 0) break; + + MoveAllItemsFrom (target, x, (i) => { + if (what.ContainsKey (i.Type)) { + var need = what[i.Type]; + var have = i.Amount; + + + if (need > have) { + what[i.Type] = need - have; + return null; + } else { + what.Remove(i.Type); + return need; + } + } + return -1; + }, alwaysAtEnd:alwaysAtEnd); + } + } + + public static void PushRequest (this IMyInventory target, List from, Dictionary what, bool alwaysAtEnd = false) { + foreach (var x in from) { + if (what.Count == 0) break; + + MoveAllItemsFrom (x, target, (i) => { + if (what.ContainsKey (i.Type)) { + var need = what[i.Type]; + var have = i.Amount; + + + if (need > have) { + what[i.Type] = need - have; + return null; + } else { + what.Remove(i.Type); + return need; + } + } + return -1; + }, alwaysAtEnd:alwaysAtEnd); + } + } + + public static double CanProduce (this MyBlueprintDefinitionBase bp, Dictionary items) { + double can_produce_times = Double.MaxValue; + foreach (var pre in bp.Prerequisites) { + var have = items.GetOr (pre.Id, MyFixedPoint.Zero); + var times = (double)have / (double)pre.Amount; + if (times < can_produce_times) { + can_produce_times = times; + if (can_produce_times == 0) { return 0; } + } + } + + return can_produce_times; + } + + + public static bool ParseHumanDefinition (string type, string subtype, out MyDefinitionId id) + { + if (type == "i" || type == "I") + { + type = "Ingot"; + } + else if (type == "o" || type == "O") + { + type = "Ore"; + } + else if (type == "c" || type == "C") + { + type = "Component"; + } + return MyDefinitionId.TryParse("MyObjectBuilder_" + type + "/" + subtype, out id); + } + public static string GetHumanName(MyDefinitionId id) + { + return id.TypeId.ToString().Replace("MyObjectBuilder_", "") + "/" + id.SubtypeName; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Log.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Log.cs new file mode 100644 index 00000000..8fcbfa5a --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Log.cs @@ -0,0 +1,88 @@ +using System; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using VRage.Game; +using VRage.Game.ModAPI; +using VRage.Utils; + +namespace Digi { + public static class Log // v1.4 + { + public static string modName = "UNNAMED"; + public static int MAX_LOGS = 500; + public static int logged = 0; + public static bool CanWriteToChat = false; + + private static IMyHudNotification notify = null; + + public static void Error(Exception e) { + Error(e.ToString()); + } + + public static void ServerError(Exception e) { + MyLog.Default.WriteLineAndConsole(e.Message + " " + e.StackTrace); + } + + public static void Error(Exception e, string printText) { + Error(printText +" "+ e.ToString(), printText); + } + + public static void Error(string msg) { + Error(msg, modName + " error - open %AppData%/SpaceEngineers"); + } + + public static void ChatError (String s, Exception e) { + ChatError (s + " " + e.ToString()); + } + + public static void ChatError (Exception e) { + ChatError (e.ToString()); + } + + public static void Test(String s) + { + ChatError(s); + } + + public static void ChatError (String s) + { + s = "SpecCores:" + s; + Info(s); + //if (!CanWriteToChat) return; + + if (logged >= MAX_LOGS) return; + logged++; + + Common.SendChatMessageToMe (s, ""); + if (logged == MAX_LOGS) + { + Common.SendChatMessageToMe("Reached limit of messages. Watch logs", ""); + } + } + + public static void Error(string msg, string printText) { + Info("ERROR: " + msg); + if (!CanWriteToChat) return; + try { + if (MyAPIGateway.Session != null) { + //MyAPIGateway.Utilities.CreateNotification(msg, 5000, MyFontEnum.Red).Show(); + if (notify == null) { + notify = MyAPIGateway.Utilities.CreateNotification(msg, 5000, MyFontEnum.Red); + } else { + notify.Text = msg; + notify.ResetAliveTime(); + } + + notify.Show(); + } + } catch (Exception e) { + Info("ERROR: Could not send notification to local client: " + e); + MyLog.Default.WriteLineAndConsole(modName + " error/exception: Could not send notification to local client: " + e); + } + } + + public static void Info(string msg) { + MyLog.Default.WriteLineAndConsole(msg); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/ModConnection.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/ModConnection.cs new file mode 100644 index 00000000..779f3018 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/ModConnection.cs @@ -0,0 +1,273 @@ +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Text; +using VRage.Game; +using VRage.Utils; + +namespace MIG.Shared.SE +{ + public static class ModConnection + { + private static string TAG = "MOD"; + private static int PORT1 = 6666666; + private static int PORT2 = 6666667; + private static Dictionary Data; + private static Dictionary>> Subscriptions = new Dictionary>>(); + private const bool DEBUGLOG = false; + private static List> RegisterQueue = new List>(); + + private static bool IsMain = false; + public static bool IsInited => ModConnection.Data != null; + + + public static void Close() + { + MyAPIGateway.Utilities.UnregisterMessageHandler(PORT1, ConnectionPortHandler); + MyAPIGateway.Utilities.UnregisterMessageHandler(PORT2, NotifyChannelHandler); + } + + public static void Init() + { + if (IsInited) throw new Exception("ModConnection already inited!"); + Log("ModConnectionComponent:MOD Init"); + MyAPIGateway.Utilities.RegisterMessageHandler(PORT1, ConnectionPortHandler); + MyAPIGateway.Utilities.RegisterMessageHandler(PORT2, NotifyChannelHandler); + + MyAPIGateway.Utilities.SendModMessage(PORT1, null); + + if (Data == null) + { + IsMain = true; + Data = new Dictionary(); + foreach (var x in RegisterQueue) //We have to react on own methods too + { + SetValue(x.Key, x.Value, crashOnDuplicate: true); + } + //We dont need react, because we are subscribed on NotifyChannel + } + else + { + foreach (var x in Data) + { + Handle(x.Key, x.Value); + } + foreach (var x in RegisterQueue) + { + SetValue(x.Key, x.Value, crashOnDuplicate: true); + } + } + } + + + private static void ConnectionPortHandler(object data) + { + Log("ConnectionPortHandler"); + if (data == null) //Request data + { + if (IsMain && Data != null) + { + Log("Request data !" + TAG); + MyAPIGateway.Utilities.SendModMessage(PORT1, Data); + } + else + { + Log("Request data Error ! " + TAG + " [" + data+"]"); + //Ignore we are not Main, or not inited yet. + } + } + else + { + var fn = data as Dictionary; + if (fn != null) + { + Log ("Arrived data! "+ TAG); + Data = fn; + } + else + { + Log("Error1 ! " + TAG); + //possible trash; + } + } + } + + public static void Log (string data) + { + if (DEBUGLOG) + { + MyLog.Default.Error($"MCon {TAG}: {data}"); + } + } + + public static void LogError (string data) + { + if (DEBUGLOG) + { + MyLog.Default.Error($"MCon {TAG}: {data}"); + } + } + + private static void NotifyChannelHandler(object data) + { + var pair = data as KeyValuePair?; + if (!pair.HasValue) + { + Log("Something wrong [Cores]"); + return; + } + var d = pair.Value; + + if (!Data.ContainsKey(d.Key)) + { + Log($"Desynchronization [Cores] [{d.Key}]/[{d.Value}] -> [{d.Key}]/[null]"); + } + else + { + if (Data[d.Key] != d.Value) + { + Log($"Desynchronization [Cores] [{d.Key}]/[{d.Value}] -> [{d.Key}]/[{Data[d.Key]}]"); + } + } + + Log($"Registered [Cores] [{d.Key}]->[{d.Value}]"); + Handle(d.Key, d.Value); + } + + private static string ALL = ""; + private static void Handle(string Name, object O) + { + Log("Handle [Cores]: " + Name); + if (Name != ALL) + { + if (Subscriptions.ContainsKey(Name)) + { + foreach (var x in Subscriptions[Name]) + { + try + { + x(Name, O); + } + catch (Exception e) + { + Log($"ModConnection [Cores]: Exception for [{Name}] : {e.ToString()}"); + } + } + } + } + + if (Subscriptions.ContainsKey(ALL)) + { + foreach (var x in Subscriptions[ALL]) + { + try + { + x(Name, O); + } + catch (Exception e) + { + Log($"ModConnection [Cores]: Exception for [{Name}] : {e.ToString()}"); + } + } + } + } + + + + + + public static void SetValue(string Name, object Data, bool crashOnDuplicate = false, bool notify = true) + { + if (ModConnection.Data == null) + { + RegisterQueue.Add(new KeyValuePair(Name, Data)); + } + else + { + if (crashOnDuplicate && ModConnection.Data.ContainsKey(Name)) + { + PrintAllData(); + throw new Exception($"Key already exists {Name} : [{ModConnection.Data[Name]}"); + } + + ModConnection.Data[Name] = Data; + if (notify) MyAPIGateway.Utilities.SendModMessage(PORT2, new KeyValuePair(Name, Data)); + } + } + + public static T Get(string Name) + { + object o; + if (Data.TryGetValue(Name, out o)) + { + if (o is T) + { + return (T)o; + } + } + + return default(T); + } + + public static void Subscribe(string Name, Action OnDataArrivedOrChanged) + { + Action catched = (a, b) => + { + try + { + OnDataArrivedOrChanged(a, b); + } + catch (Exception e) + { + Log($"{Name}:" + e.ToString()); + } + }; + + Subscriptions.GetOrNew(Name).Add(catched); + if (Data.ContainsKey(Name)) + { + try + { + catched(Name, Data[Name]); + } + catch (Exception e) + { + LogError($"ModConnection [Cores]:OnDataArrivedOrChanged {Name} {Data[Name]} error: {e}"); + } + + } + } + + public static void Subscribe(string Name, Action OnDataArrivedOrChanged) + { + Subscribe(Name, (name, data) => OnDataArrivedOrChanged((T) data)); + } + + public static void Subscribe(string Name, T intance, Action OnDataArrivedOrChanged) + { + Subscribe(Name, (name, data) => OnDataArrivedOrChanged((T) data)); + } + + public static void SetValueAndSubscribe(string Name, T Data, Action OnDataArrivedOrChanged, bool crashOnDuplicate = true) + { + SetValue(Name, Data, crashOnDuplicate); + Subscribe(Name, OnDataArrivedOrChanged); + } + + public static void SubscribeToAll(Action OnDataArrivedOrChanged) + { + Subscribe(ALL, OnDataArrivedOrChanged); + } + + public static void PrintAllData() + { + var sb = new StringBuilder("ModConnection [Cores]:\n"); + foreach (var x in Data) + { + sb.AppendLine($"{x.Key} -> {x.Value}"); + } + + Log(sb.ToString()); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/NAPI.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/NAPI.cs new file mode 100644 index 00000000..d06e65c3 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/NAPI.cs @@ -0,0 +1,1448 @@ +using Sandbox.Game.Entities; +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Sandbox.Game.EntityComponents; +using VRage.Game.Components; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRageMath; +using Sandbox.Definitions; +using VRage; +using VRage.Game; +using VRage.Collections; +using VRage.Game.Entity; +using VRage.ObjectBuilders; + +namespace MIG.Shared.SE { + + public static class NAPI { + private const int FREEZE_FLAG = 4; + public static bool isFrozen(this IMyEntity grid) { return ((int)grid.Flags | FREEZE_FLAG) == (int)grid.Flags; } + public static void setFrozen(this IMyEntity grid) { grid.Flags = grid.Flags | (EntityFlags)FREEZE_FLAG; } + public static void setUnFrozen(this IMyEntity e) { e.Flags &= ~(EntityFlags)FREEZE_FLAG; } + + + + public static Vector3D GetWorldPosition (this IMySlimBlock block) { + var box = new BoundingBoxD (); + block.GetWorldBoundingBox(out box); + return box.Center; + } + + [Obsolete("Use IsDedicated instead")] + public static bool isTorchServer (this IMySession session) { + return MyAPIGateway.Utilities.IsDedicated; + } + + public static bool IsDedicated (this IMySession session) { + return MyAPIGateway.Utilities.IsDedicated; + } + + public static string SubtypeName (this IMyCubeBlock block) { + return block.SlimBlock.BlockDefinition.Id.SubtypeName; + } + + public static T Definition(this MyObjectBuilder_CubeBlock ob) where T : MyCubeBlockDefinition + { + return (T)MyDefinitionManager.Static.GetCubeBlockDefinition(ob); + } + + public static MyCubeBlockDefinition Definition(this MyObjectBuilder_CubeBlock ob) + { + return MyDefinitionManager.Static.GetCubeBlockDefinition(ob); + } + + public static string GetHumanName(this MyDefinitionId id) + { + return id.ToString().Replace("MyObjectBuilder_", ""); + } + + public static string GetHumanInfo(this Dictionary kv) + { + var sb = new StringBuilder(); + foreach (var kv2 in kv) + { + sb.Append($" {kv2.Value}x{kv2.Key.GetHumanName()} "); + } + return sb.ToString(); + } + + + public static long BuiltBy(this IMyCubeBlock block) { + return block.SlimBlock.BuiltBy; + } + + public static bool IsSameFactionLeader(this IMyCubeBlock block) + { + var builtBy = block.BuiltBy(); + var f = builtBy.PlayerFaction(); + var pl = MyAPIGateway.Session.LocalHumanPlayer; + var my = pl.GetFaction(); + if (f != my) return false; + if (my == null) return pl.IdentityId == builtBy; + return my.IsLeader(pl.IdentityId) || my.IsFounder(pl.IdentityId); + } + + public static T As(this long entityId) { + IMyEntity entity; + if (!MyAPIGateway.Entities.TryGetEntityById(entityId, out entity)) return default(T); + if (entity is T) { return (T) entity; } else { return default(T); } + } + + public static T GetAs (this IMyEntity entity) where T : MyGameLogicComponent { + var logic = entity.GameLogic; + var t = logic as T; + if (t != null) return t; + var cmp = logic as MyCompositeGameLogicComponent; + return cmp?.GetAs(); + } + + public static void FindFatBlocks(this IMyCubeGrid grid, List blocks, Func filter) { + var gg = grid as MyCubeGrid; + var ff = gg.GetFatBlocks(); + foreach (var x in ff) { + var fat = (IMyCubeBlock) x; + if (filter(fat)) { blocks.Add((T) fat); } + } + } + + public static void OverFatBlocks(this IMyCubeGrid grid, Action action) { + var gg = grid as MyCubeGrid; + var ff = gg.GetFatBlocks(); + foreach (var x in ff) { + var fat = (IMyCubeBlock) x; + action(fat); + } + } + + public static List GetConnectedGrids(this IMyCubeGrid grid, GridLinkTypeEnum with, List list = null, bool clear = false) { + if (list == null) list = new List(); + if (clear) list.Clear(); + MyAPIGateway.GridGroups.GetGroup(grid, with, list); + return list; + } + + public static IMyFaction PlayerFaction(this long playerId) { return MyAPIGateway.Session.Factions.TryGetPlayerFaction(playerId); } + + public static IMyPlayer GetPlayer(this IMyCharacter character) { return MyAPIGateway.Players.GetPlayerControllingEntity(character); } //CAN BE NULL IF IN COCKPIT + public static IMyPlayer GetPlayer(this IMyShipController cockpit) { return MyAPIGateway.Players.GetPlayerControllingEntity(cockpit); } + public static IMyPlayer GetPlayer(this IMyIdentity Identity) { + IMyPlayer player = null; + MyAPIGateway.Players.GetPlayers(null, (x) => { + if (x.IdentityId == Identity.IdentityId) + { + player = x; + } + return false; + }); + return player; + } + + public static IMyIdentity GetIdentity(this long playerId) + { + IMyIdentity ident = null; + MyAPIGateway.Players.GetAllIdentites(null, (x)=> + { + if (playerId == x.IdentityId) + { + ident = x; + } + return false; + }); + return ident; + } + + public static void SetPower(this IMyCubeBlock cubeBlock, float power) + { + cubeBlock?.ResourceSink?.SetMaxRequiredInputByType(MyResourceDistributorComponent.ElectricityId, power); + cubeBlock?.ResourceSink?.SetRequiredInputByType(MyResourceDistributorComponent.ElectricityId, power); + } + + public static float CurrentPowerInput(this IMySlimBlock block) + { + var fat = block.FatBlock; + if (fat == null) return 0; + return fat.ResourceSink.CurrentInputByType(MyResourceDistributorComponent.ElectricityId); + } + + public static float MaxRequiredPowerInput(this IMySlimBlock block) + { + var fat = block.FatBlock; + if (fat == null) return 0; + return fat.ResourceSink.MaxRequiredInputByType(MyResourceDistributorComponent.ElectricityId); + } + + public static float CurrentPowerInput(this IMyCubeBlock fat) + { + if (fat == null) return 0; + return fat.ResourceSink.CurrentInputByType(MyResourceDistributorComponent.ElectricityId); + } + + public static float MaxRequiredPowerInput(this IMyCubeBlock fat) + { + if (fat == null) return 0; + return fat.ResourceSink.MaxRequiredInputByType(MyResourceDistributorComponent.ElectricityId); + } + + public static bool IsOnline (this IMyPlayerCollection players, long identity) + { + bool contains = false; + players.GetPlayers (null, (x)=>{ + if (x.IdentityId == identity) + { + contains = true; + } + return false; + }); + + return contains; + } + + public static bool IsControllingCockpit (this IMyShipController cockpit) + { + if (cockpit.IsMainCockpit) + { + return true; + } + else if (cockpit.ControllerInfo != null && cockpit.ControllerInfo.Controller != null && cockpit.ControllerInfo.Controller.ControlledEntity != null) + { + return true; + } + + return false; + } + + public static bool IsMainControlledCockpit(this IMyShipController cockpit) + { + return cockpit.ControllerInfo != null && cockpit.ControllerInfo.Controller != null && cockpit.ControllerInfo.Controller.ControlledEntity != null; + } + + public static IMyCubeGrid GetMyControlledGrid(this IMySession session) { + var cock = MyAPIGateway.Session.Player.Controller.ControlledEntity as IMyCockpit; + if (cock == null) return null; + return cock.CubeGrid; + } + + public static IMyFaction Faction(this long factionId) + { + return MyAPIGateway.Session.Factions.TryGetFactionById(factionId); + } + + public static bool isBot (this IMyIdentity identity) + { + return MyAPIGateway.Players.TryGetSteamId(identity.IdentityId) == 0; + } + + public static bool IsUserAdmin(this ulong SteamUserId) + { + return MyAPIGateway.Session.IsUserAdmin(SteamUserId); + } + + public static MyPromoteLevel PromoteLevel (this ulong SteamUserId) + { + var PlayersList = new List(); + MyAPIGateway.Players.GetPlayers(PlayersList); + foreach (var Player in PlayersList.Where(Player => Player.SteamUserId == SteamUserId)) + { + return Player.PromoteLevel; + } + return MyPromoteLevel.None; + } + + public static IMyIdentity Identity (this ulong SteamUserId) + { + IMyIdentity identity = null; + MyAPIGateway.Multiplayer.Players.GetAllIdentites(null, (x)=> + { + if (identity != null) return false; + var st = MyAPIGateway.Multiplayer.Players.TryGetSteamId(x.IdentityId); + if (st == SteamUserId) + { + identity = x; + } + return false; + }); + return identity; + } + + public static long IdentityId (this ulong SteamUserId) + { + return SteamUserId.Identity()?.IdentityId ?? 0; + } + + public static BoundingBoxD GetAABB (this List grids) + { + var aabb1 = grids[0].PositionComp.WorldAABB; + BoundingBoxD aabb = new BoundingBoxD(aabb1.Min, aabb1.Max); + for (var x=1; x GetEntitiesInSphere (this IMyEntities entities, Vector3D pos, double radius, Func filter = null) { + var sphere = new BoundingSphereD(pos, radius); + var list = entities.GetEntitiesInSphere(ref sphere); + if (filter != null) { list.RemoveAll((x)=>!filter(x)); } + return list; + } + + + public static void SendMessageToOthersProto(this IMyMultiplayer multi, ushort id, object o, bool reliable = true) { + var bytes = MyAPIGateway.Utilities.SerializeToBinary(o); + multi.SendMessageToOthers(id, bytes, reliable); + } + + // Calculates how much components are welded for block + public static void GetRealComponentsCost(this IMySlimBlock block, Dictionary dictionary, Dictionary temp = null) + { + if (temp == null) temp = new Dictionary(); + var components = (block.BlockDefinition as MyCubeBlockDefinition).Components; + foreach (var component in components) + { + string name = component.Definition.Id.SubtypeName; + int count = component.Count; + + if (dictionary.ContainsKey(name)) dictionary[name] += count; + else dictionary.Add(name, count); + } + + temp.Clear(); + block.GetMissingComponents(temp); + + foreach (var component in temp) + { + string name = component.Key; + int count = component.Value; + + if (dictionary.ContainsKey(name)) dictionary[name] -= count; + else dictionary.Add(name, count); + } + } + + public static bool ParseHumanDefinition(string type, string subtype, out MyDefinitionId id) + { + if (type == "i" || type == "I") + { + type = "Ingot"; + } + else if (type == "o" || type == "O") + { + type = "Ore"; + } + else if (type == "c" || type == "C") + { + type = "Component"; + } + return MyDefinitionId.TryParse("MyObjectBuilder_" + type + "/" + subtype, out id); + } + + public static ListReader GetFatBlocks (this IMyCubeGrid grid) + { + return ((MyCubeGrid)grid).GetFatBlocks(); + } + } + + public static class RandomSugar { + public static Vector3 NextVector(this Random random, float x, float y, float z) + { + x = x * (float)(2*random.NextDouble()-1d); + y = y * (float)(2*random.NextDouble()-1d); + z = z * (float)(2*random.NextDouble()-1d); + return new Vector3(x, y, z); + } + + public static double NextDouble(this Random random, double min, double max) + { + return min + (max - min) * random.NextDouble(); + } + + public static float NextFloat(this Random random, double min, double max) + { + return (float)(min + (max - min) * random.NextDouble()); + } + + public static T Next (this Random random, IEnumerable array) + { + if (array.Count() == 0) return default (T); + return array.ElementAt(random.Next()%array.Count()); + } + + public static T NextWithChance(this Random random, List array, Func func, bool returnLastAsDefault = false) + { + if (array.Count == 0) return default(T); + for (int x=0; x(this string d) + { + var data = Convert.FromBase64String(d); + return MyAPIGateway.Utilities.SerializeFromBinary(data); + } + + public static string ToBase64Binary(this T data) + { + var s = MyAPIGateway.Utilities.SerializeToBinary(data); + return Convert.ToBase64String(s); + } + + public static bool TryGetStorageData(this IMyEntity entity, Guid guid, out T value, bool protoBuf = false) + { + if (entity.Storage == null) + { + value = default(T); + return false; + } + else + { + var d = entity.GetStorageData(guid); + if (d == null) + { + value = default(T); + return false; + } + + try + { + value = protoBuf ? FromBase64Binary(d) : MyAPIGateway.Utilities.SerializeFromXML(d); + return true; + } + catch (Exception e) + { + value = default(T); + return false; + } + } + } + + public static string GetStorageData(this IMyEntity entity, Guid guid) { + if (entity.Storage == null) return null; + string data; + if (entity.Storage.TryGetValue(guid, out data)) { + return data; + } else { + return null; + } + } + + public static T GetStorageData(this IMyEntity entity, Guid guid, bool protoBuf = false) + { + T data; + if (TryGetStorageData(entity, guid, out data, protoBuf)) + { + return data; + } + + return default(T); + } + + public static string GetAndSetStorageData(this IMyEntity entity, Guid guid, string newData) { + var data = GetStorageData(entity, guid); + SetStorageData(entity, guid, newData); + return data; + } + + public static void SetStorageData(this IMyEntity entity, Guid guid, String data) { + if (entity.Storage == null && data == null) { + return; + } + entity.GetOrCreateStorage().SetValue(guid, data); + } + + public static void SetStorageData(this IMyEntity entity, Guid guid, T data, bool protoBuf = false) + { + if (data == null) + { + SetStorageData(entity, guid, null); + return; + } + + var s = protoBuf ? ToBase64Binary(data) : MyAPIGateway.Utilities.SerializeToXML(data); + SetStorageData(entity, guid, s); + } + } + + + public static class Sharp { + private static Regex R = new Regex("{([^}]*)}"); + public static string Replace(this string pattern, Func replacer) + { + string s = pattern; + while (true) + { + var news = R.Replace(s, match => + { + return replacer(match.Groups[1].Value); + }); + + if (news == s) + { + return news; + } + else + { + s = news; + } + } + } + + public static int NextExcept(this Random r, HashSet ignored, int max) { + while (true) { + var n = r.Next(max); + if (!ignored.Contains(n)) { return n; } + } + } + + public static string[] ToStrings(this string s, params string[] by) + { + return s.Split(by, StringSplitOptions.RemoveEmptyEntries); + } + + public static int[] ToInts(this string s) + { + if (string.IsNullOrEmpty(s)) return new int[0]; + var ints = s.Split(new string[] { ",", " "}, StringSplitOptions.RemoveEmptyEntries); + var list = new List(); + + int res; + foreach (var f in ints) + { + if (int.TryParse(f, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) + { + list.Add(res); + } + } + return list.ToArray(); + } + + public static long[] ToLongs(this string s) + { + if (string.IsNullOrEmpty(s)) return new long[0]; + var ints = s.Split(new string[] { ",", " " }, StringSplitOptions.RemoveEmptyEntries); + var list = new List(); + + long res; + foreach (var f in ints) + { + if (long.TryParse(f, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) + { + list.Add(res); + } + } + return list.ToArray(); + } + + private static float[] toFloats(this string s) + { + if (string.IsNullOrEmpty(s)) return new float[0]; + var floats = s.Split(new string[]{",", " "}, StringSplitOptions.RemoveEmptyEntries); + var list = new List(); + + float res; + foreach (var f in floats) + { + if (float.TryParse(f, NumberStyles.Any, CultureInfo.InvariantCulture, out res)) + { + list.Add(res); + } + } + return list.ToArray(); + } + + public static DateTime utcZero = new DateTime(1970, 1, 1); + public static DateTime y2020 = new DateTime(2020, 1, 1); + + public static float Lerp2 (float Current, float Desired, float Speed) + { + if (Current < Desired) + { + Current += Speed; + if (Current > Desired) + { + Current = Desired; + } + } + else + { + Current -= Speed; + if (Current < Desired) + { + Current = Desired; + } + } + return Current; + } + + public static float toRadian (this float v) + { + return (float)(v * Math.PI / 180d); + } + + public static double toRadian(this double v) + { + return (v * Math.PI / 180d); + } + + public static float toDegree (this float v) + { + return (float)(v / Math.PI * 180d); + } + + public static double toDegree (this double v) + { + return (v / Math.PI * 180d); + } + + + public static K GetOr(this IDictionary dict, T t, K k) { + if (dict.ContainsKey(t)) { + return dict[t]; + } else { + return k; + } + } + + public static K GetOrNew(this IDictionary dict, T t) where K : new() { + if (!dict.ContainsKey(t)) + { + var k = new K(); + dict[t] = k; + return dict[t]; + } + else + { + return dict[t]; + } + } + + public static bool HasFlags(this int x, int f) + { + return (x | f) == x; + } + + public static long msTimeStamp () { + return (long)(DateTime.UtcNow.Subtract(utcZero)).TotalMilliseconds; + } + + public static string fixZero (this double num) { + return String.Format ("{0:N2}", num); + } + + public static string fixZero(this float num) + { + return String.Format("{0:N2}", num); + } + + public static T ElementAtOrLast(this T[] array, int at) + { + var min = Math.Min(at, array.Length-1); + return array[min]; + } + + public static T ElementAtOrLast(this IList array, int at) + { + var min = Math.Min(at, array.Count-1); + return array[min]; + } + + public static void Add(this IDictionary dict1, KeyValuePair kv) + { + dict1.Add(kv.Key, kv.Value); + } + + + public static void Mlt (this IDictionary dict, IDictionary dict2, bool onlyIfContains = false) { + foreach (var kv in dict2) + { + dict.Mlt(kv.Key, kv.Value, onlyIfContains); + } + } + + public static void Mlt (this IDictionary dict, T key, float value, bool onlyIfContains = false) { + if (!dict.ContainsKey(key)) { + if (!onlyIfContains) + { + dict[key] = value; + } + } else { + dict[key] = dict[key] * value; + } + } + + public static void Sum (this IDictionary dict, T key, double value) { + if (!dict.ContainsKey(key)) { + dict[key] = value; + } else { + dict[key] = dict[key] + value; + } + } + + public static void Sum (this IDictionary dict, T key, float value) { + if (!dict.ContainsKey(key)) { + dict[key] = value; + } else { + dict[key] = dict[key] + value; + } + } + + public static void Sum (this IDictionary dict, T key, MyFixedPoint value) { + if (!dict.ContainsKey(key)) { + dict[key] = value; + } else { + dict[key] = dict[key] + value; + } + } + + public static void Sum (this IDictionary dict, T key, int value) { + if (!dict.ContainsKey(key)) { + dict[key] = value; + } else { + dict[key] = dict[key] + value; + } + } + + public static void Sum (this IDictionary dict, IDictionary dict2) { + foreach (var d in dict2) + { + dict.Sum(d.Key, d.Value); + } + } + + public static void SetValues (this IDictionary dict, IDictionary value) + { + foreach (var y in value) + { + dict[y.Key] = y.Value; + } + } + + public static bool Remove(this IDictionary dict1, K key, V value) + { + if (dict1.ContainsKeyValue(key, value)) + { + dict1.Remove(key); + return true; + } + + return false; + } + + public static bool ContainsKeyValue(this IDictionary dict1, K key, V value) + { + V value2; + if (dict1.TryGetValue(key, out value2)) + { + return value.Equals(value2); + } + + return false; + } + + public static bool ContainsKeyValue(this IDictionary dict1, KeyValuePair kv) + { + return ContainsKeyValue(dict1, kv.Key, kv.Value); + } + + + public static void Minus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] -= other[i.Key]; + } + } + } + + public static void Plus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] += other[i.Key]; + } else { + x.Add(i.Key, other[i.Key]); + } + } + } + + public static void Minus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] -= other[i.Key]; + } else + { + x[i.Key] = -other[i.Key]; + } + } + } + + + + public static void Minus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] -= other[i.Key]; + } + else + { + x[i.Key] = -other[i.Key]; + } + } + } + + public static void Plus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] += other[i.Key]; + } else { + x.Add(i.Key, other[i.Key]); + } + } + } + + public static void Plus(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] += other[i.Key]; + } else { + x.Add(i.Key, other[i.Key]); + } + } + } + + public static void PlusIfContains(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] += other[i.Key]; + } + } + } + + public static void MinusIfContains(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] -= other[i.Key]; + } + } + } + + public static void Plus(this IDictionary x,T otherKey, int otherValue) { + if (x.ContainsKey(otherKey)) { + x[otherKey] += otherValue; + } else { + x.Add(otherKey, otherValue); + } + } + + + public static void Max(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] = Math.Max(x[i.Key], other[i.Key]); + } else { + x[i.Key] = other[i.Key]; + } + } + } + + public static void MaxIfContains(this IDictionary x, IDictionary other) { + foreach (var i in other) { + if (x.ContainsKey(i.Key)) { + x[i.Key] = Math.Max(x[i.Key], other[i.Key]); + } + } + } + + public static bool ContainsOneOfKeys(this IDictionary x, ICollection collection) { + foreach (var t in collection) + { + if (x.ContainsKey(t)) + { + return true; + } + } + + return false; + } + + public static void SumIfNotContains(this Dictionary values, Dictionary values2) + { + foreach (var u in values2) + { + if (values.ContainsKey(u.Key)) + { + values[u.Key] = u.Value; + } + } + } + + public static Dictionary SumDuplicates(this ICollection values) + { + var dict = new Dictionary(); + foreach (var u in values) + { + dict.Sum(u, 1); + } + + return dict; + } + + public static void RemoveDuplicates(this IDictionary dict1, IDictionary dict2) + { + foreach (var kv in dict2) + { + dict1.Remove(kv.Key, kv.Value); + } + } + + public static void RemoveDuplicateKeys(this IDictionary dict1, IDictionary dict2) + { + foreach (var kv in dict2) + { + if (dict1.ContainsKey(kv.Key)) + { + dict1.Remove(kv.Key); + } + } + } + + public static D GetDuplicates(this IDictionary dict1, IDictionary dict2) where D : Dictionary, new() + { + var dict3 = new D(); + foreach (var kv in dict1) + { + if (dict2.ContainsKeyValue(kv)) + { + dict3.Add(kv); + } + } + + return dict3; + } + + public static void RemoveDuplicatesBoth(this Dictionary dict1, Dictionary dict2) + { + var list = new List(); + foreach (var kv in dict2) + { + if (dict1.Remove(kv.Key, kv.Value)) + { + list.Add(kv.Key); + } + } + + foreach (var k in list) + { + dict2.Remove(k); + } + } + } + + public static class Bytes { + public static int Pack(this byte[] bytes, int pos, int what) { + var b1 = BitConverter.GetBytes(what); + bytes[pos + 0] = b1[0]; + bytes[pos + 1] = b1[1]; + bytes[pos + 2] = b1[2]; + bytes[pos + 3] = b1[3]; + return 4; + } + + public static int Pack(this byte[] bytes, int pos, byte what) { + bytes[pos] = what; + return 1; + } + + public static int Pack(this byte[] bytes, int pos, uint what) { + var b1 = BitConverter.GetBytes(what); + bytes[pos + 0] = b1[0]; + bytes[pos + 1] = b1[1]; + bytes[pos + 2] = b1[2]; + bytes[pos + 3] = b1[3]; + return 4; + } + public static int Pack(this byte[] bytes, int pos, float what) + { + var b1 = BitConverter.GetBytes(what); + bytes[pos + 0] = b1[0]; + bytes[pos + 1] = b1[1]; + bytes[pos + 2] = b1[2]; + bytes[pos + 3] = b1[3]; + return 4; + } + + public static int Pack(this byte[] bytes, int pos, long what) { + var b1 = BitConverter.GetBytes(what); + bytes[pos + 0] = b1[0]; + bytes[pos + 1] = b1[1]; + bytes[pos + 2] = b1[2]; + bytes[pos + 3] = b1[3]; + bytes[pos + 4] = b1[4]; + bytes[pos + 5] = b1[5]; + bytes[pos + 6] = b1[6]; + bytes[pos + 7] = b1[7]; + return 8; + } + + public static int Pack(this byte[] bytes, int pos, ulong what) { + var b1 = BitConverter.GetBytes(what); + bytes[pos + 0] = b1[0]; + bytes[pos + 1] = b1[1]; + bytes[pos + 2] = b1[2]; + bytes[pos + 3] = b1[3]; + bytes[pos + 4] = b1[4]; + bytes[pos + 5] = b1[5]; + bytes[pos + 6] = b1[6]; + bytes[pos + 7] = b1[7]; + return 8; + } + + public static long Long(this byte[] bytes, int pos) { return BitConverter.ToInt64(bytes, pos); } + public static ulong ULong(this byte[] bytes, int pos) { return BitConverter.ToUInt64(bytes, pos); } + public static double Double(this byte[] bytes, int pos) { return BitConverter.ToDouble(bytes, pos); } + public static int Int(this byte[] bytes, int pos) { return BitConverter.ToInt32(bytes, pos); } + public static uint UInt(this byte[] bytes, int pos) { return BitConverter.ToUInt32(bytes, pos); } + public static float Float(this byte[] bytes, int pos) { return BitConverter.ToSingle(bytes, pos); } + public static short Short(this byte[] bytes, int pos) { return BitConverter.ToInt16(bytes, pos); } + public static ushort UShort(this byte[] bytes, int pos) { return BitConverter.ToUInt16(bytes, pos); } + } + + + public static class NapiRelations + { + public const int MEMBERSHIP_NO_FACTION = -2; + public const int MEMBERSHIP_NOT_MEMBER = -1; + public const int MEMBERSHIP_APPLICANT = 0; + public const int MEMBERSHIP_MEMBER = 1; + public const int MEMBERSHIP_LEADER = 2; + public const int MEMBERSHIP_FOUNDER = 3; + + public static IMyFaction GetBuilderFaction (this IMyCubeBlock block) { + return MyAPIGateway.Session.Factions.TryGetPlayerFaction (block.BuiltBy()); + } + + public static IMyFaction GetFaction (this IMyPlayer pl) { + return MyAPIGateway.Session.Factions.TryGetPlayerFaction (pl.IdentityId); + } + public static IMyFaction GetFaction (long pl) { + return MyAPIGateway.Session.Factions.TryGetPlayerFaction (pl); + } + public static IMyFaction GetFaction (this IMyIdentity pl) { + return MyAPIGateway.Session.Factions.TryGetPlayerFaction (pl.IdentityId); + } + + public static int GetRelation(this long u1, long u2) { + return MyIDModule.GetRelationPlayerPlayer(u1, u2).AsNumber(); + } + + public static int GetRelationToBuilder(this IMySlimBlock block, long userId) { + return MyIDModule.GetRelationPlayerBlock(userId, block.BuiltBy, MyOwnershipShareModeEnum.Faction).AsNumber(); + } + + public static int GetRelationToOwnerOrBuilder(this IMySlimBlock block, long userId) { + return MyIDModule.GetRelationPlayerBlock(userId, block.OwnerId != 0 ? block.OwnerId : block.BuiltBy, MyOwnershipShareModeEnum.Faction).AsNumber(); + } + + public static int GetRelationToOwnerOrBuilder(this IMyCubeBlock block, long userId) { + return MyIDModule.GetRelationPlayerBlock(userId, block.OwnerId != 0 ? block.OwnerId : block.BuiltBy(), MyOwnershipShareModeEnum.Faction).AsNumber(); + } + + public static long GetOwnerOrBuilder (this IMySlimBlock block) { + return block.OwnerId != 0 ? block.OwnerId : block.BuiltBy; + } + + public static long GetOwnerOrBuilder (this IMyCubeBlock block) { + return block.OwnerId != 0 ? block.OwnerId : block.BuiltBy(); + } + + public static int GetRelation(this IMyCubeGrid cubeGrid, long userId) { + return GetUserRelation(cubeGrid, userId).AsNumber(); + } + + public static int AsNumber(this MyRelationsBetweenPlayerAndBlock relation) { + if (relation == MyRelationsBetweenPlayerAndBlock.Owner || relation == MyRelationsBetweenPlayerAndBlock.FactionShare) { + return 1; + } else if (relation == MyRelationsBetweenPlayerAndBlock.Enemies) { + return -1; + } else return 0; + } + + public static int AsNumber(this MyRelationsBetweenPlayers relation) { + if (relation == MyRelationsBetweenPlayers.Self) return 2; + if (relation == MyRelationsBetweenPlayers.Allies) return 1; + if (relation == MyRelationsBetweenPlayers.Enemies) return -1; + return 0; + } + + public static bool IsEnemy(this IMyCubeGrid grid, long userId) { + return grid.GetUserRelation(userId) == MyRelationsBetweenPlayerAndBlock.Enemies; + } + + public static bool IsEnemy(this IMyCharacter u, long userId) { + return MyIDModule.GetRelationPlayerBlock(u.EntityId, userId, MyOwnershipShareModeEnum.Faction) == MyRelationsBetweenPlayerAndBlock.Enemies; + } + + public static MyRelationsBetweenPlayerAndBlock GetUserRelation(this IMyCubeGrid cubeGrid, long userId) { + var enemies = false; + var neutral = false; + try { + foreach (var key in cubeGrid.BigOwners) { + + var owner = MyAPIGateway.Entities.GetEntityById(key); + //Log.Info("Owner:" + owner); + + var relation = MyIDModule.GetRelationPlayerBlock(key, userId, MyOwnershipShareModeEnum.Faction); + if (relation == MyRelationsBetweenPlayerAndBlock.Owner || relation == VRage.Game.MyRelationsBetweenPlayerAndBlock.FactionShare) { + return relation; + } else if (relation == MyRelationsBetweenPlayerAndBlock.Enemies) { + enemies = true; + } else if (relation == MyRelationsBetweenPlayerAndBlock.Neutral) { + neutral = true; + } + } + } catch { + //The list BigOwners could change while iterating -> a silent catch + } + if (enemies) return MyRelationsBetweenPlayerAndBlock.Enemies; + if (neutral) return MyRelationsBetweenPlayerAndBlock.Neutral; + return MyRelationsBetweenPlayerAndBlock.NoOwnership; + } + + public static int GetRelation(this IMySlimBlock block, long userId) { + return MyIDModule.GetRelationPlayerBlock(userId, block.OwnerId, MyOwnershipShareModeEnum.Faction).AsNumber(); + } + + public static bool IsOwnedByFactionLeader (this IMyCubeBlock block) { + if (block.OwnerId == block.BuiltBy()) { + var faction = MyAPIGateway.Session.Factions.TryGetPlayerFaction (block.BuiltBy()); + if (faction != null) { + return faction.FounderId == block.BuiltBy(); + } else { + return false; + } + } else { + return false; + } + } + + public static bool IsFactionLeaderOrFounder (this long user) { + var faction = MyAPIGateway.Session.Factions.TryGetPlayerFaction (user); + if (faction != null) { + return faction.GetMemberShip (user) > 1; + } + return false; + } + + public static int GetFactionMemberShip (this long user) { + var faction = MyAPIGateway.Session.Factions.TryGetPlayerFaction (user); + if (faction != null) { + return faction.GetMemberShip (user); + } + return MEMBERSHIP_NO_FACTION; + } + public static int GetMemberShip (this IMyFaction faction, long user) { + if (faction.FounderId == user) return MEMBERSHIP_FOUNDER; + foreach (var x in faction.Members) { + if (x.Key == user) { + if (x.Value.IsLeader) return MEMBERSHIP_LEADER; + return 1; + } + } + + if (faction.JoinRequests.ContainsKey(user)) return MEMBERSHIP_APPLICANT; + return MEMBERSHIP_NOT_MEMBER; + } + } + + public static class InventoryUt + { + public static Dictionary GetBlockPrice (this IMySlimBlock slim, Dictionary dict = null) { + if (dict == null) dict = new Dictionary(); + + var cmps = (slim.BlockDefinition as MyCubeBlockDefinition).Components; + + foreach (var xx in cmps) { + var id = xx.Definition.Id; + var c = xx.Count; + if (dict.ContainsKey(id)) { + dict[id] += c; + } else { + dict.Add (id, c); + } + } + + return dict; + } + + public static Dictionary GetBlockLeftNeededComponents (this IMySlimBlock slim, Dictionary dict = null, Dictionary temp = null) { + if (dict == null) dict = new Dictionary(); + + var cmps = (slim.BlockDefinition as MyCubeBlockDefinition).Components; + + temp.Clear(); + foreach (var xx in cmps) { + var id = xx.Definition.Id; + var c = xx.Count; + if (temp.ContainsKey(id)) { + temp[id] += c; + } else { + temp.Add (id, c); + } + } + + foreach (var x in temp) { + var id = x.Key; + var has = slim.GetConstructionStockpileItemAmount (id); + var need = x.Value; + var left = need - has; + if (left > 0) { + if (dict.ContainsKey(id)) { + dict[id] += left; + } else { + dict.Add (id, left); + } + } + } + + return dict; + } + + public static MyFixedPoint GetLeftVolume(this IMyInventory inventory) { + return inventory.MaxVolume-inventory.CurrentVolume; + } + + public static double GetLeftVolumeInLiters(this IMyInventory inventory) { + return ((double)inventory.GetLeftVolume())*1000d; + } + + public static double GetFilledRatio(this IMyInventory inventory) { + return (double)inventory.CurrentVolume / (double)inventory.MaxVolume; + } + + public static Dictionary CountItems(this IMyInventory inventory, Dictionary d = null) { + var items = inventory.GetItems(); + + if (d == null) { + d = new Dictionary(); + } + + foreach (var x in items) { + var id = x.Content.GetId(); + if (!d.ContainsKey(id)) { + d.Add(x.Content.GetId(), x.Amount); + } else { + d[id] += x.Amount; + } + + } + + return d; + } + + /// + /// Adds items to many invetories, modifies `items` param, leaving info about how much items wasn't spawned + /// + /// + /// + public static void AddItems(this List inventories, Dictionary items) + { + var keys = new List(items.Keys); + var zero = (MyFixedPoint)0.0001; + + if (inventories.Count == 0 || keys.Count == 0) return; + + foreach (var y in keys) + { + foreach (var x in inventories) + { + if (!items.ContainsKey(y)) continue; + if (!x.CanItemsBeAdded(zero, y)) continue; + + + var amount = items[y]; + var am = ((MyInventoryBase)x).ComputeAmountThatFits(y); + if (am >= (MyFixedPoint)amount) + { + x.AddItem(y, amount); + items.Remove(y); + break; + } + else + { + x.AddItem(y, am); + items[y] = amount - (double)am; + } + } + } + } + + public static Dictionary CountItemsD(this IMyInventory inventory, Dictionary d = null) + { + var items = inventory.GetItems(); + + if (d == null) + { + d = new Dictionary(); + } + + foreach (var x in items) + { + var id = x.Content.GetId(); + if (!d.ContainsKey(id)) + { + d.Add(x.Content.GetId(), (double)x.Amount); + } + else + { + d[id] += (double)x.Amount; + } + + } + + return d; + } + + public static void AddItem(this IMyInventory inv, MyDefinitionId id, double amount) { + inv.AddItems((MyFixedPoint)amount, (MyObjectBuilder_PhysicalObject)MyObjectBuilderSerializer.CreateNewObject(id)); + } + public static void AddItem(this IMyInventory inv, MyDefinitionId id, MyFixedPoint amount) { + inv.AddItems(amount, (MyObjectBuilder_PhysicalObject)MyObjectBuilderSerializer.CreateNewObject(id)); + } + + public static bool RemoveAmount(this IMyInventory inv, Dictionary toRemove) + { + if (toRemove == null || toRemove.Count == 0) return false; + + var items = inv.GetItems(); + var l = items.Count; + var k = 0; + + for (var i = 0; i < l; i++) + { + var itm = items[i]; + var id = itm.Content.GetId(); + if (toRemove.ContainsKey(id)) + { + var am = toRemove[id]; + if (itm.Amount <= am) + { + am -= itm.Amount; + toRemove[id] = am; + inv.RemoveItemsAt(i - k); + k++; + } + else + { + toRemove.Remove(id); + inv.RemoveItemAmount(itm, am); + } + } + } + + return toRemove.Count == 0; + } + + public static bool RemoveAmount(this IMyInventory inv, Dictionary toRemove) + { + if (toRemove == null || toRemove.Count == 0) return false; + + var items = inv.GetItems(); + var l = items.Count; + var k = 0; + + for (var i = 0; i < l; i++) + { + var itm = items[i]; + var id = itm.Content.GetId(); + if (toRemove.ContainsKey(id)) + { + var am = toRemove[id]; + if ((double)itm.Amount <= am) + { + am -= (double)itm.Amount; + toRemove[id] = am; + inv.RemoveItemsAt(i - k); + k++; + } + else + { + toRemove.Remove(id); + inv.RemoveItemAmount(itm, (MyFixedPoint)am); + } + } + } + + return toRemove.Count == 0; + } + + public static MyFixedPoint RemoveAmount (this IMyInventory inv, MyDefinitionId id, double amount) { + if (amount <= 0) return (MyFixedPoint)amount; + + var items = inv.GetItems(); + var l = items.Count; + var k = 0; + var am = (MyFixedPoint)amount; + for (var i = 0; i + { + private ushort port; + private Action handler; + public Connection(ushort port, Action handler) + { + this.port = port; + this.handler = handler; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(port, Handler); + GameBase.AddUnloadAction(() => { MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(port, Handler); }); + } + + public void SendMessageToServer(T data, bool reliable = true) + { + //Log.ChatError ("SendMessageToServer"); + var bdata = MyAPIGateway.Utilities.SerializeToBinary(data); + MyAPIGateway.Multiplayer.SendMessageToServer(port, bdata, reliable); + } + + public void SendMessageToOthers(T data, bool reliable = true) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(data); + MyAPIGateway.Multiplayer.SendMessageToOthers(port, bdata, reliable); + } + + public void SendMessageTo(T data, ulong SteamID, bool reliable = true) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(data); + MyAPIGateway.Multiplayer.SendMessageTo(port, bdata, SteamID, reliable); + } + + public void Handler(ushort HandlerId, byte[] bdata, ulong PlayerSteamId, bool isFromServer) + { + try + { + var data = MyAPIGateway.Utilities.SerializeFromBinary(bdata); + handler(data, PlayerSteamId, isFromServer); + } catch (Exception e) + { + Log.ChatError("Handle message: " + HandlerId + " " + PlayerSteamId + " " + e); + } + + } + } + + public class Sync + { + public const byte REQUEST = 254; + public const byte SEND = 255; + + private ushort port; + private Action handler; + private Action handler2; + private Func getter; + private Func entityLogicGetter = null; + + public Sync(ushort port, Func getter, Action _handler, Action _handler2 = null, Func entityLogicGetter = null) + { + this.port = port; + this.entityLogicGetter = entityLogicGetter ?? this.entityLogicGetter; + this.handler = (z, t, b, u, boo) => _handler(z, t, u, boo); + if (_handler2 != null) + { + this.handler2 = (t, b, u, boo) => _handler2(t, u, boo); + } + this.getter = getter; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(port, Handler); + GameBase.AddUnloadAction(() => { MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(port, Handler); }); + } + + public Sync(ushort port, Func getter, Action handler, Action handler2 = null, Func entityLogicGetter = null) + { + this.entityLogicGetter = entityLogicGetter ?? this.entityLogicGetter; + this.port = port; + this.handler = handler; + this.handler2 = handler2; + this.getter = getter; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(port, Handler); + GameBase.AddUnloadAction(() => { MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(port, Handler); }); + } + + public void Handler(ushort HandlerId, byte[] bdata, ulong PlayerSteamId, bool isFromServer) + { + try + { + var data = MyAPIGateway.Utilities.SerializeFromBinary(bdata); + if (data == null) { + return; + } + if (data.type == REQUEST) + { + var z = entityLogicGetter(data.entityId); + if (z != null) + { + var t = getter.Invoke(z); + SendMessageToOthers(data.entityId, t, true); + } + + return; + } + + if (data.type != REQUEST) + { + var z = entityLogicGetter(data.entityId); + if (z != null) + { + var dataz = MyAPIGateway.Utilities.SerializeFromBinary(data.data); + try + { + handler.Invoke(z, dataz, data.type, PlayerSteamId, isFromServer); + } + catch (Exception e) + { + Log.ChatError($"Sync error! HandlerId:[{HandlerId}] isFromServer[{isFromServer}] PlayerSteamId:[{PlayerSteamId}] ex:{e}"); + } + } + else + { + if (handler2 != null) + { + var dataz = MyAPIGateway.Utilities.SerializeFromBinary(data.data); + handler2.Invoke(dataz, data.type, PlayerSteamId, isFromServer); + } + } + } + } + catch (Exception ex) + { + Log.Error($"Sync Handler Error! HandlerId:[{HandlerId}] isFromServer[{isFromServer}] PlayerSteamId:[{PlayerSteamId}] ex:{ex}"); + throw; + } + } + + public void SendMessageToServer(long entityId, T data, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new EntitySync(entityId, type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageToServer(port, bdata, reliable); + } + + public void SendMessageToOthers(long entityId, T data, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new EntitySync(entityId, type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageToOthers(port, bdata, reliable); + } + + public void SendMessageTo(long entityId, T data, ulong SteamID, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new EntitySync(entityId, type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageTo(port, bdata, SteamID, reliable); + } + + public void RequestData(long entityId, bool reliable = true) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new EntitySync(entityId, REQUEST, null)); + MyAPIGateway.Multiplayer.SendMessageToServer(port, bdata, reliable); + } + } + + + public class StaticSync + { + public const byte REQUEST = 254; + public const byte SEND = 255; + + private ushort port; + private Action handler; + private Func getter; + + public StaticSync(ushort port, Func getter, Action _handler) + { + this.port = port; + this.handler = (t, b, u, boo) => _handler(t, u, boo); + this.getter = getter; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(port, Handler); + GameBase.AddUnloadAction(() => { MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(port, Handler); }); + } + + public StaticSync(ushort port, Func getter, Action handler) + { + this.port = port; + this.handler = handler; + this.getter = getter; + MyAPIGateway.Multiplayer.RegisterSecureMessageHandler(port, Handler); + GameBase.AddUnloadAction(() => { MyAPIGateway.Multiplayer.UnregisterSecureMessageHandler(port, Handler); }); + } + + public void Handler(ushort HandlerId, byte[] bdata, ulong PlayerSteamId, bool isFromServer) + { + try + { + var data = MyAPIGateway.Utilities.SerializeFromBinary(bdata); + if (data.type == REQUEST) + { + var t = getter.Invoke(); + SendMessageToOthers(t, true); + return; + } + + if (data.type != REQUEST) + { + var dataz = MyAPIGateway.Utilities.SerializeFromBinary(data.data); + handler.Invoke(dataz, data.type, PlayerSteamId, isFromServer); + } + } + catch (Exception ex) + { + Log.Error($"Sync Handler Error! HandlerId:[{HandlerId}] isFromServer[{isFromServer}] ex:{ex} PlayerSteamId:[{PlayerSteamId}]"); + throw; + } + } + + public void SendMessageToServer(T data, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new NonEntitySync(type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageToServer(port, bdata, reliable); + } + + public void SendMessageToOthers(T data, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new NonEntitySync(type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageToOthers(port, bdata, reliable); + } + + public void SendMessageTo(T data, ulong SteamID, bool reliable = true, byte type = SEND) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new NonEntitySync(type, MyAPIGateway.Utilities.SerializeToBinary(data))); + MyAPIGateway.Multiplayer.SendMessageTo(port, bdata, SteamID, reliable); + } + + public void RequestData(bool reliable = true) + { + var bdata = MyAPIGateway.Utilities.SerializeToBinary(new NonEntitySync(REQUEST, null)); + MyAPIGateway.Multiplayer.SendMessageToServer(port, bdata, reliable); + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Other.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Other.cs new file mode 100644 index 00000000..d5c8442c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Other.cs @@ -0,0 +1,391 @@ +using System; +using System.Collections.Generic; +using Sandbox.Definitions; +using Sandbox.ModAPI; +using VRage.Game; +using VRageMath; +using Sandbox.Game.Entities; +using VRage.Game.ModAPI; +using VRage; +using Digi; +using VRage.ObjectBuilders; +using Sandbox.ModAPI.Weapons; +using System.Linq; +using MIG.Shared.CSharp; + +namespace MIG.Shared.SE { + static class Players { + private static List playersCache = new List(); + private static long lastTime = 0; + public static List GetCachedPlayers(this IMyPlayerCollection collection, Func filter) { + if (SharpUtils.msTimeStamp() - lastTime > 1000) { + MyAPIGateway.Players.GetPlayers(playersCache, (x) => true); + lastTime = SharpUtils.msTimeStamp(); + } + return new List(playersCache.Where(filter)); + } + } + + static class Other { + public static long FindPlayerByCharacterId(long charId) { + return charId.As()?.GetPlayer().IdentityId ?? 0; + } + + public static IMyPlayer GetPlayer (long player) { + var players = MyAPIGateway.Players.GetCachedPlayers(x => x.IdentityId == player); + return players.Count > 0 ? players[0] : null; + } + + public static bool PlayerIsOnline (long player) { + var players = MyAPIGateway.Players.GetCachedPlayers(x => x.IdentityId == player); + return players.Count > 0; + } + + public static IMyPlayer findPlayerByName(string name) { + var players = MyAPIGateway.Players.GetCachedPlayers((x)=>x.DisplayName.Equals(name)); + if (players.Count > 0) return players[0]; + else return null; + } + + public static void GetBlocksInsideSphere(this IMyCubeGrid mgrid, ref BoundingSphereD sphere, ICollection blocks, Func filter = null) + { + if (mgrid.PositionComp == null) + return; + var grid = (MyCubeGrid)mgrid; + + Vector3D localCenter; + var matInv = grid.PositionComp.WorldMatrixNormalizedInv; + Vector3D.Transform(ref sphere.Center, ref matInv, out localCenter); + var localSphere = new BoundingSphere(localCenter, (float)sphere.Radius); + var box = BoundingBox.CreateFromSphere(localSphere); + Vector3D min = box.Min; + Vector3D max = box.Max; + Vector3I start = new Vector3I((int)Math.Round(min.X * grid.GridSizeR), (int)Math.Round(min.Y * grid.GridSizeR), (int)Math.Round(min.Z * grid.GridSizeR)); + Vector3I end = new Vector3I((int)Math.Round(max.X * grid.GridSizeR), (int)Math.Round(max.Y * grid.GridSizeR), (int)Math.Round(max.Z * grid.GridSizeR)); + + Vector3I startIt = Vector3I.Min(start, end); + Vector3I endIt = Vector3I.Max(start, end); + + if ((endIt - startIt).Volume() < grid.BlocksCount) + { + Vector3I_RangeIterator it = new Vector3I_RangeIterator(ref startIt, ref endIt); + var pos = it.Current; + MyCube cube; + for (; it.IsValid(); it.GetNext(out pos)) + { + if (grid.TryGetCube(pos, out cube)) + { + var slim = (IMySlimBlock)cube.CubeBlock; + var aabb = new BoundingBox(slim.Min * grid.GridSize - grid.GridSizeHalf, slim.Max * grid.GridSize + grid.GridSizeHalf); + if (aabb.Intersects(localSphere) && (filter == null || filter.Invoke(cube.CubeBlock))) + { + blocks.Add(cube.CubeBlock); + } + } + } + } + else + { + mgrid.GetBlocks(null, (value) => + { + var aabb = new BoundingBox(value.Min * grid.GridSize - grid.GridSizeHalf, value.Max * grid.GridSize + grid.GridSizeHalf); + if (aabb.Intersects(localSphere) && (filter == null || filter.Invoke(value))) + { + blocks.Add(value); + } + return false; + }); + } + } + + public static long GetPlayerByCharacter(long p) { + var aa = MyAPIGateway.Entities.GetEntityById(p); + if (aa is IMyCharacter) { + var player = Other.findPlayerByName(aa.DisplayName); + return player==null ? 0 : player.PlayerID; + } else if (aa is IMyPlayer) { + Log.Info("GetPlayerByCharacter. It is player, not character"); + return p; + } else { + Log.Error("Not player:" +(aa==null ? "null" : aa.ToString()) + " id:" + p + " " + MyAPIGateway.Session.Player.PlayerID ); + return 0; + } + } + + + public static bool BuilderIsOnline (this IMyCubeBlock block) { + return PlayerIsOnline(block.BuiltBy()); + } + + public static List GetFactionsWithOnlinePlayers() { + List factions = new List(); + List players = new List(); + MyAPIGateway.Players.GetPlayers(players, x=> !x.IsBot); + HashSet playersIds = new HashSet(); + + foreach (var i in players) { + playersIds.Add(i.PlayerID); + } + + var total = 0; + foreach (var x in MyAPIGateway.Session.Factions.Factions.Values) { + bool found = false; + + foreach (var y in x.Members) { + var pid = y.Value.PlayerId; + if (playersIds.Contains(pid)) { + if (!found) { + factions.Add(x); + } + found = true; + playersIds.Remove(pid); + } + } + } + + return factions; + } + + public static void Copy (this MatrixD m, ref Vector3D vec) { + vec.X = m.M41; + vec.Y = m.M42; + vec.Z = m.M43; + } + + public static long GetToolOwner (this IMyEngineerToolBase hnd) { + if (hnd != null && hnd.OwnerId != 0) { + return GetPlayerByCharacter(hnd.OwnerId); + } else { + return 0L; + } + } + + public static long GetToolOwner(this IMyHandheldGunObject hnd) { + if (hnd != null && hnd.OwnerId != 0) { + return GetPlayerByCharacter(hnd.OwnerId); + } else { + return 0L; + } + } + + + public static IMyCharacter GetCharacter(this IMyHandheldGunObject hnd) { + if (hnd != null && hnd.OwnerId != 0) { + var ch = MyEntities.GetEntityByIdOrDefault (hnd.OwnerId, null); + return ch as IMyCharacter; + } else { + return null; + } + } + + public static bool IsMyTool(this IMyHandheldGunObject hnd) { + var pl = MyAPIGateway.Session.Player; + + if (pl==null) return false; + return hnd.GetToolOwner() == pl.IdentityId; + } + + + + public static T LoadWorldFile(string file) { + file += ".xml"; + if (MyAPIGateway.Utilities.FileExistsInWorldStorage(file, typeof(T))) { + try + { + using (var reader = MyAPIGateway.Utilities.ReadFileInWorldStorage(file, typeof(T))) + { + return MyAPIGateway.Utilities.SerializeFromXML(reader.ReadToEnd()); + } + } catch (Exception exc) { + Log.Error(exc); + return default(T); + } + } else { + return default(T); + } + } + + public static T LoadWorldFile(string file, Func defaultGenerator) + { + file += ".xml"; + if (MyAPIGateway.Utilities.FileExistsInWorldStorage(file, typeof(T))) + { + try + { + using (var reader = MyAPIGateway.Utilities.ReadFileInWorldStorage(file, typeof(T))) + { + return MyAPIGateway.Utilities.SerializeFromXML(reader.ReadToEnd()); + } + } + catch (Exception exc) + { + Log.Error(exc); + return defaultGenerator(); + } + } + else + { + return defaultGenerator(); + } + } + + public static T LoadFirstModFile(string name, Func defaultGenerator) + { + name = $"Data/{name}.xml"; + foreach (var Mod in MyAPIGateway.Session.Mods) + { + if (!MyAPIGateway.Utilities.FileExistsInModLocation(name, Mod)) continue; + + try + { + using (var reader = MyAPIGateway.Utilities.ReadFileInModLocation(name, Mod)) + { + return MyAPIGateway.Utilities.SerializeFromXML(reader.ReadToEnd()); + } + } + catch (Exception exc) + { + FrameExecutor.addDelayedLogic(100, (x) => Log.ChatError("Loading default settings " + exc)); + Log.Error(exc); + return defaultGenerator(); + } + } + + return defaultGenerator(); + } + + + public static String LoadPlainWorldFile(string file) { + if (MyAPIGateway.Utilities.FileExistsInWorldStorage(file, typeof(T))) { + try + { + using (var reader = MyAPIGateway.Utilities.ReadFileInWorldStorage(file, typeof(T))) + { + return reader.ReadToEnd(); + } + } catch (Exception exc) { + Log.Error(exc); + return ""; + } + } else { + return ""; + } + } + + public static bool SaveWorldFile(string file, T settings) { + file += ".xml"; + try { + using (var writer = MyAPIGateway.Utilities.WriteFileInWorldStorage(file, typeof(T))) { + writer.Write(MyAPIGateway.Utilities.SerializeToXML(settings)); + } + return true; + } catch (Exception exc) { + Log.Error(exc); + return false; + } + } + + + public static IMyPlayer GetNearestPlayer(Vector3D checkCoords) { + IMyPlayer thisPlayer = null; + double distance = Double.MaxValue; + + var list = new List(); + MyAPIGateway.Players.GetPlayers(list); // MES_SessionCore.PlayerList???? + + //Log.Info("TotalPlayers:" + list.Count); + + foreach (var player in list) { + if (player.Character == null || player.IsBot == true) { + //Log.Info("Character == null || player.IsBot" + list.Count); + continue; + } + + var currentDist = Vector3D.Distance(player.GetPosition(), checkCoords); + + + //Log.Info("Check Character" + currentDist + " " + distance + " " + player); + if (currentDist < distance) { + thisPlayer = player; + distance = currentDist; + } + + } + return thisPlayer; + } + + public static bool spawnPrefab(this MyPrefabDefinition prefab, Vector3D pos, Vector3 forward, Vector3 up, long playerId, Action beforeCreated, Action onCreated, MyOwnershipShareModeEnum share = MyOwnershipShareModeEnum.Faction) + { + try + { + var gridOB = prefab.CubeGrids[0]; + + gridOB.PositionAndOrientation = new MyPositionAndOrientation(pos, forward, up); + MyAPIGateway.Entities.RemapObjectBuilder(gridOB); + beforeCreated?.Invoke(gridOB); + MyAPIGateway.Entities.CreateFromObjectBuilderParallel(gridOB, true, (x) => + { + var grid = x as IMyCubeGrid; + if (playerId != 0) + { + grid.ChangeGridOwnership(playerId, share); + } + onCreated(grid); + }); + return true; + } + catch (Exception e) + { + Log.Error(e); + return false; + } + } + + public static bool spawnPrefab(this MyPrefabDefinition prefab, Vector3D pos, Vector3 forward, Vector3 up, long playerId, Action onCreated, MyOwnershipShareModeEnum share = MyOwnershipShareModeEnum.Faction) { + try { + var gridOB = prefab.CubeGrids[0]; + var pos2 = MyAPIGateway.Entities.FindFreePlace(pos, prefab.BoundingBox.Size.Max()/2) ?? Vector3.Zero; + if (pos2 == Vector3.Zero) { + return false; + } + gridOB.PositionAndOrientation = new MyPositionAndOrientation(pos2, forward, up); + MyAPIGateway.Entities.RemapObjectBuilder(gridOB); + + MyAPIGateway.Entities.CreateFromObjectBuilderParallel(gridOB, true, (x)=> { + var grid = x as IMyCubeGrid; + if (playerId != 0) { + grid.ChangeGridOwnership(playerId, share); + } + onCreated(grid); + }); + return true; + } catch (Exception e) { + Log.Error(e); + return false; + } + } + + + public static MyObjectBuilder_CubeGrid projectPrefab(this MyPrefabDefinition prefab, Vector3D pos, Vector3D direction, long playerId, MyOwnershipShareModeEnum share = MyOwnershipShareModeEnum.Faction) { + try { + var x = new MyObjectBuilder_CubeGrid(); + var y = new MyCubeGrid(); + y.GetObjectBuilder(); + + var gridOB = prefab.CubeGrids[0]; + var pos2 = MyAPIGateway.Entities.FindFreePlace(pos, prefab.BoundingBox.Size.Max()); + if (pos2 == null) + { + return null; + } + // + //gridOB.PositionAndOrientation = new MyPositionAndOrientation(pos2 ?? Vector3D.Zero, Vector3.Forward, Vector3.Up); + //MyAPIGateway.Entities.RemapObjectBuilder(gridOB); + return gridOB; + } catch (Exception e) { + Log.Error(e); + return null; + } + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Pair.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Pair.cs new file mode 100644 index 00000000..48c8ce66 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Pair.cs @@ -0,0 +1,48 @@ +using ProtoBuf; + +namespace MIG.Shared.CSharp { + [ProtoContract] + public class Pair { + [ProtoMember(1)] + public K k; + + [ProtoMember(2)] + public V v; + + public Pair() + { + } + + public Pair(K k, V v) { + this.k = k; + this.v = v; + } + + public override int GetHashCode() + { + return (k?.GetHashCode() ?? 0) + (v?.GetHashCode() ?? 0); + } + + public override bool Equals(object obj) + { + var pair = obj as Pair; + if (pair == null) return false; + + return (((pair.k != null && k != null && pair.k.Equals(k)) || (pair.k == null && k == null)) && ((pair.v != null && v != null && pair.v.Equals(v)) || (pair.v == null && v == null))); + + } + } + + public class Tripple { + public K k; + public V v; + public T t; + + public Tripple(K k, V v, T t) { + this.k = k; + this.v = v; + this.t = t; + } + } + +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Physics.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Physics.cs new file mode 100644 index 00000000..047e56c5 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Physics.cs @@ -0,0 +1,56 @@ +using Sandbox.ModAPI; +using VRage.Game; +using VRage.Utils; +using VRageMath; + +namespace MIG.Shared.SE { + public class PhysicsHelper { + public static bool areSameDirection (Vector3D a, Vector3D b) { + a.Normalize(); + b.Normalize(); + var d = a - b; + if (d.Length () < 0.05) { + return true; + } + return false; + } + + public static void Draw (Color c, Vector3D start, Vector3D vec, float thick = 0.05f, string material = "Square") { + if (!MyAPIGateway.Session.isTorchServer()) { + var n = (float)vec.Normalize(); + MyTransparentGeometry.AddLineBillboard(MyStringId.GetOrCompute(material), c, start, vec, n, thick);//, MyBillboard.BlendTypeEnum.Standard);//, -1, 100f); + } + } + + public static void DrawPoint(Color c, Vector3D start, float radius, float angle = 0, string material = "Square") + { + if (!MyAPIGateway.Session.isTorchServer()) + { + MyTransparentGeometry.AddPointBillboard(MyStringId.GetOrCompute(material), c, start, radius, angle);//, MyBillboard.BlendTypeEnum.Standard);//, -1, 100f); + } + } + + public static void DrawLine(Color c, Vector3D start, Vector3D vec, float thick, MyStringId material) + { + var n = (float)vec.Normalize(); + MyTransparentGeometry.AddLineBillboard(material, c, start, vec, n, thick); + } + + public static void DrawLineFromTo(Color c, Vector3D start, Vector3D end, float thick, MyStringId material) + { + var v = (end - start); + var n = (float)v.Normalize(); + MyTransparentGeometry.AddLineBillboard(material, c, start, v, n, thick); + } + + public static void DrawLineFromTo(Color c, Vector3D start, Vector3D end, float thick) + { + DrawLineFromTo (c, start, end, thick, MyStringId.GetOrCompute("Square")); + } + + public static void DrawPoint(Color c, Vector3D start, float radius, float angle, MyStringId material) + { + MyTransparentGeometry.AddPointBillboard(material, c, start, radius, angle); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Relations.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Relations.cs new file mode 100644 index 00000000..a8ab9a09 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Relations.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using Sandbox.ModAPI; +using VRage.Game; +using Sandbox.Game.Entities; +using VRage.Game.ModAPI; +using Digi; +using MIG.Shared.CSharp; +using Sandbox.ModAPI.Weapons; + +namespace MIG.Shared.SE { + static class Relations { + public static Dictionary GetOnlinePlayers (this IMyFaction faction, Dictionary set, List pl = null) { + if (pl == null) { + pl = new List(); + MyAPIGateway.Players.GetPlayers (pl, null); + } + + foreach (IMyPlayer x in pl) { + foreach (var y in faction.Members) { + if (y.Key == x.IdentityId) { + set.Set (x, y.Value.IsFounder ? 2 : y.Value.IsLeader ? 1 : 0); + } + } + } + + return set; + } + + + + public static long FindBot(String name) { + var y = MyAPIGateway.Multiplayer.Players; + var pl = new List(); + y.GetAllIdentites(pl, x =>{ return x.DisplayName.Equals(name); }); + if (pl.Count == 1) { + return pl[0].IdentityId; + } else return 0L; + } + + public static bool IsFriend(this MyDamageInformation damage, IMySlimBlock block) { + return damage.GetRelation (block) == 1; + } + + public static int GetRelation(this MyDamageInformation damage, IMySlimBlock block) { + var dealer = GetDamageDealer (damage); + if (dealer == 0) return 0; + + if (block.OwnerId != 0) return block.CubeGrid.GetRelation(dealer); + else return block.GetRelationToBuilder(dealer); + } + + public static bool IsByHandGrinder (this MyDamageInformation damage) { + var attacker = MyAPIGateway.Entities.GetEntityById(damage.AttackerId); + var hnd = attacker as IMyEngineerToolBase; + if (hnd != null) return hnd.DefinitionId.SubtypeName.Contains("Grinder"); + var hnd2 = attacker as IMyHandheldGunObject; + if (hnd2 != null) return hnd2.DefinitionId.SubtypeName.Contains("Grinder"); + + return false; + } + + public static long GetDamageDealer (this MyDamageInformation damage) { + var attacker = MyAPIGateway.Entities.GetEntityById(damage.AttackerId); + var hnd = attacker as IMyEngineerToolBase; + if (hnd != null) return hnd.GetToolOwner(); + var hnd2 = attacker as IMyHandheldGunObject; + if (hnd2 != null) return hnd2.GetToolOwner(); + var pl = attacker as IMyCharacter; + if (pl != null) return Other.FindPlayerByCharacterId(pl.EntityId); + var cb = attacker as IMySlimBlock; + if (cb != null) return cb.OwnerId != 0 ? cb.OwnerId : cb.BuiltBy; + var cb2 = attacker as IMyCubeBlock; + if (cb2 != null) return cb2.OwnerId != 0 ? cb2.OwnerId : cb2.BuiltBy(); + + return 0; + } + + + + public static void OwnGrid(this IMyCubeGrid y, long transferTo, MyOwnershipShareModeEnum shareOptions) { + y.ChangeGridOwnership(transferTo, shareOptions); + } + + public static void OwnBlocks(this IMyCubeGrid y, long transferTo, MyOwnershipShareModeEnum shareOptions, Func apply) { //MyOwnershipShareModeEnum.None + var blocks = new List(); + y.GetBlocks(blocks); + + Log.Info("OwnBlocks:" + y.DisplayName + " : " + blocks.Count); + + foreach (var b in blocks) { + var fat = b.FatBlock; + if (fat != null) { + var own = apply(b); + if (own) { + if (fat is MyCubeBlock) { + (fat as MyCubeBlock).ChangeOwner(transferTo, shareOptions); + Log.Info("Change ownership:" + blocks.Count + " " + b.OwnerId + " " + " " + b.GetType().Name + " " + fat.GetType().Name + " " + (b is MyCubeBlock)); + } + } + } + } + } + + public static void OwnBlock(this IMySlimBlock b, long transferTo, MyOwnershipShareModeEnum shareOptions) { + var fat = b.FatBlock; + if (fat != null && fat is MyCubeBlock) { + (fat as MyCubeBlock).ChangeOwner(transferTo, shareOptions); + } + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Sessions.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Sessions.cs new file mode 100644 index 00000000..c74923ef --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Sessions.cs @@ -0,0 +1,82 @@ +using VRage.Game; +using VRage.Game.Components; + +namespace MIG.Shared.SE +{ + public abstract class SessionComponentWithSettings : MySessionComponentBase + { + protected T Settings; + public override void Init(MyObjectBuilder_SessionComponent sessionComponent) + { + Settings = Other.LoadWorldFile(GetFileName(), GetDefault); + } + public override void SaveData() + { + Other.SaveWorldFile(GetFileName(), Settings); + base.SaveData(); + } + + protected abstract T GetDefault(); + protected abstract string GetFileName(); + } + + public abstract class SessionComponentWithSyncSettings : MySessionComponentBase + { + protected StaticSync Sync; + public T Settings; + public override void Init(MyObjectBuilder_SessionComponent sessionComponent) + { + Settings = Other.LoadWorldFile(GetFileName(), GetDefault); + Sync = new StaticSync(GetPort(), SettingsGetter, HandleData); + } + + public virtual T SettingsGetter() + { + return Settings; + } + + public override void SaveData() + { + base.SaveData(); + Other.SaveWorldFile(GetFileName(), Settings); + } + + protected abstract void HandleData(T data, byte action, ulong player, bool isFromServer); + protected abstract T GetDefault(); + protected abstract string GetFileName(); + protected abstract ushort GetPort(); + } + + public abstract class SessionComponentExternalSettings : MySessionComponentBase + { + protected StaticSync Sync; + public T Settings; + public override void Init(MyObjectBuilder_SessionComponent sessionComponent) + { + Settings = Other.LoadFirstModFile(GetFileName(), GetDefault); + //Other.SaveWorldFile(GetFileName(), Settings); + } + protected abstract T GetDefault(); + protected abstract string GetFileName(); + } + + public abstract class SessionComponentWithSyncAndExternalSettings : MySessionComponentBase + { + protected StaticSync Sync; + public T Settings; + public override void Init(MyObjectBuilder_SessionComponent sessionComponent) + { + Settings = Other.LoadFirstModFile(GetFileName(), GetDefault); + Sync = new StaticSync(GetPort(), SettingsGetter, HandleData); + } + + public virtual T SettingsGetter() + { + return Settings; + } + protected abstract void HandleData(T data, byte action, ulong player, bool isFromServer); + protected abstract T GetDefault(); + protected abstract string GetFileName(); + protected abstract ushort GetPort(); + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/SharpUtils.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/SharpUtils.cs new file mode 100644 index 00000000..c8c4b775 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/SharpUtils.cs @@ -0,0 +1,538 @@ +using Sandbox.ModAPI; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using Digi; +using MIG.Shared.SE; +using MIG.SpecCores; +using VRage.Game.ModAPI; +using VRageMath; + +namespace MIG.Shared.CSharp { + + static class ToStr + { + public static string printContent(this List dict) { + StringBuilder sb = new StringBuilder(); + sb.Append("List["); + foreach (var x in dict) { + sb.Append(x).Append(",\n"); + } + sb.Append("]"); + return sb.ToString(); + } + + public static string printContent(this List dict) { + StringBuilder sb = new StringBuilder(); + sb.Append("List["); + foreach (var x in dict) { + sb.Append(x.DisplayName + "/" + x.PlayerID).Append(", "); + } + sb.Append("]"); + return sb.ToString(); + } + + public static string printContent(this List dict) { + StringBuilder sb = new StringBuilder(); + sb.Append("List["); + foreach (var x in dict) { + sb.Append(x.Name + "/" +x.FactionId).Append(", "); + } + sb.Append("]"); + return sb.ToString(); + } + + + public static string printContent(this List dict) { + StringBuilder sb = new StringBuilder(); + sb.Append("List["); + foreach (var x in dict) { + sb.Append("{").Append(x.ItemId).Append("/").Append(x.Amount).Append("/").Append(x.Blueprint).Append("},\n"); + } + sb.Append("]"); + return sb.ToString(); + } + + public static Dictionary Copy(this Dictionary dict) + { + return new Dictionary(dict); + } + } + + static class SharpUtils { + + [Obsolete("Use Sharp.msTimeStamp")] + public static long msTimeStamp () { + return (long)(DateTime.UtcNow.Subtract(utcZero)).TotalMilliseconds; + } + + public static DateTime utcZero = new DateTime(1970, 1, 1); + public static DateTime y2020 = new DateTime(2020, 1, 1); + + public static double Degree (Vector3D v1, Vector3D v2) + { + return Math.Acos(v1.Dot(v2) / (v1.Length() * v2.Length())).toDegree(); + } + + public static double Degree2 (Vector3D v1, Vector3D v2) + { + var d = Degree(v1, v2); + + if ((v1+v2).LengthSquared () < (v1 - v2).LengthSquared()) + { + d*=-1; + } + return d; + } + + public static long timeStamp () { + return (long)(DateTime.UtcNow.Subtract(utcZero)).TotalSeconds; + } + + public static long timeUtcDif() + { + return Math.Abs((long)DateTime.UtcNow.Subtract(DateTime.Now).TotalSeconds); + } + + + + public static TimeSpan StripMilliseconds(this TimeSpan time) + { + return new TimeSpan(time.Days, time.Hours, time.Minutes, time.Seconds); + } + + + public static void AddOrRemove(this HashSet set, T data, bool add) { + if (add) { set.Add(data); } else { set.Remove(data); } + } + + public static void RemoveWhere(this Dictionary set, Func filter) + { + var list = new List(); + foreach (var t in set) + { + if (filter(t.Key, t.Value)) + { + list.Add(t.Key); + } + } + + foreach (var t in list) + { + set.Remove(t); + } + } + + public static string Print(this IDictionary dict, string separator = "\n", Func Where = null) { + StringBuilder sb = new StringBuilder(); + sb.Append("Dict["); + foreach (var x in dict) { + if (Where != null && !Where.Invoke(x.Key, x.Value)) continue; + sb.Append(x.Key).Append("->").Append(x.Value).Append(separator); + } + sb.Append("]"); + return sb.ToString(); + } + + public static string Print(this List list) + { + StringBuilder sb = new StringBuilder(); + sb.Append("List["); + foreach (var x in list) + { + if (x == null) + { + sb.Append("null"); + } + else + { + sb.Append(x); + } + sb.Append(", "); + } + sb.Append("]"); + return sb.ToString(); + } + + + + + public static string toHumanWeight (this double num) { + if (num <0.000001) return String.Format ("{0:N2} µg", num *1000000000); + if (num <0.001) return String.Format ("{0:N2} mg", num *1000000); + if (num <1) return String.Format ("{0:N2} g", num *1000); + if (num <1000) return String.Format ("{0:N2} kg", num); + if (num <1000000) return String.Format ("{0:N2} t", num /1000); + if (num <1000000000) return String.Format ("{0:N2} kt", num /1000000); + if (num <1000000000000) return String.Format ("{0:N2} Mt", num /1000000000); + if (num <1000000000000000) return String.Format ("{0:N2} Gt", num /1000000000000); + return "TONS"; + } + + + public static string toHumanWeight2 (this double num) + { + if (num <1000) return String.Format(CultureInfo.InvariantCulture, "{0:N0} Kg", num); + if (num <1000000) return String.Format (CultureInfo.InvariantCulture, "{0:N1} Ton", num / 1000).Replace(".0", ""); + if (num <1000000000) return String.Format (CultureInfo.InvariantCulture, "{0:N1} kTon", num /1000000).Replace(".0", ""); + if (num <1000000000000) return String.Format (CultureInfo.InvariantCulture, "{0:N1} MTon", num /1000000000).Replace(".0", ""); + if (num <1000000000000000) return String.Format (CultureInfo.InvariantCulture, "{0:N1} GTon", num /1000000000000).Replace(".0", ""); + return "TONS"; + } + + public static string toHumanQuantity (this double num) { + if (num <1000) return String.Format ("{0:N2}", num); + if (num <1000000) return String.Format ("{0:N2} K", num /1000); + if (num <1000000000) return String.Format ("{0:N2} M", num /1000000); + return "TONS"; + } + + public static string toPhysicQuantity (this double num, String s) { + var k = 1000d; + if (num 1000) return $"{num / 1000 :N0} GW"; + if (Math.Abs(num) > 1) return $"{num :N2} MW"; + if (Math.Abs(num) > 0.001) return $"{num * 1000 :N0} KW"; + return $"{num * 1000000 :N0} W"; + } + + public static string toHumanQuantityVolume (this double num) + { + if (Math.Abs(num) > 1000000000) return $"{num / 1000000000 :N2} GL"; + if (Math.Abs(num) > 1000000) return $"{num / 1000000 :N2} ML"; + if (Math.Abs(num) > 1000) return $"{num / 1000 :N2} kL"; + if (Math.Abs(num) > 100) return $"{num / 100 :N2} hL"; + if (Math.Abs(num) > 10) return $"{num / 10 :N2} daL"; + if (Math.Abs(num) > 1) return $"{num :N2} L"; + if (Math.Abs(num) > 0.1) return $"{num * 10 :N2} dL"; + if (Math.Abs(num) > 0.01) return $"{num * 100 :N2} cL"; + return $"{num * 1000 :N2} mL"; + } + + public static string toPercentage (this double num) { + return String.Format ("{0:N2}%", num*100); + } + + public static string toMlt (this double num) { + return String.Format ("{0:N2}", num); + } + + public static string toHumanTime (this double num) { + if (num <120) return String.Format ("{0:N0} s", num); + if (num <3600) return String.Format ("{0:N0} min", num /60); + if (num <3600*24) return String.Format ("{0:N0} h", num /3600); + return String.Format ("{0:N0} days", num/3600/24); + } + public static string toHumanTime2 (this int num, bool isFullWithDays = false) { + if (num < 60) return num + "s"; + if (num < 3600) return num / 60 + "m " + num % 60 + "s"; + if (num < 3600 * 24) return num / 3600 + "h " + (num / 60) % 60 + "m " + num % 60 + "s"; + if (num / 3600 / 24 == 1) return isFullWithDays ? "1 day " + num / 3600 + "h " + (num / 60) % 60 + "m " + num % 60 + "s" : "1 day"; + return isFullWithDays ? num / 3600 / 24 + " days " + num / 3600 % 24 + "h " + (num / 60) % 60 + "m " + num % 60 + "s" : num / 3600 / 24 + " days"; + } + + + public static string Print (this IDictionary dict, Func printer, string separator = "\r\n") + { + var s = new StringBuilder(); + int c = 0; + foreach (var kv in dict) + { + s.Append(printer (kv.Key, kv.Value)); + if (c != dict.Count-1) + { + s.Append(separator); + } + } + + return s.ToString(); + } + + + + public static bool IsOneKeyMoreThan (this IDictionary buffer, IDictionary maxLimits) + { + foreach (var y in buffer) + { + if (y.Value > maxLimits[y.Key]) + { + return true; + } + } + return false; + } + + + + + + public static Action TryCatch(this Action func, string debugName) + { + return (a) => + { + try + { + func.Invoke(a); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + } + }; + } + + public static Action TryCatch(this Action func, string debugName) + { + return (a,b) => + { + try + { + func.Invoke(a,b); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + } + }; + } + + public static Action TryCatch(this Action func, string debugName) + { + return (a,b,c) => + { + try + { + func.Invoke(a,b,c); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + } + }; + } + + public static Action TryCatch(this Action func, string debugName) + { + return (a,b,c,d) => + { + try + { + func.Invoke(a,b,c,d); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + } + }; + } + + public static Func TryCatch(this Func func, string debugName) + { + return (a) => + { + try + { + return func.Invoke(a); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + return default(R); + } + }; + } + + + public static Func TryCatch(this Func func, string debugName) + { + return (a,b) => + { + try + { + return func.Invoke(a,b); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + return default(R); + } + }; + } + + public static Func TryCatch(this Func func, string debugName) + { + return (a,b,c) => + { + try + { + return func.Invoke(a,b,c); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + return default(R); + } + }; + } + + public static Func TryCatch(this Func func, string debugName) + { + return (a,b,c,d) => + { + try + { + return func.Invoke(a,b,c,d); + } + catch (Exception e) + { + Log.ChatError(debugName, e); + return default(R); + } + }; + } + + + + public static bool GetLess (this IDictionary current, IDictionary maxLimits, Dictionary shouldPunish) + { + bool has = false; + foreach (var y in current) + { + try + { + var max = maxLimits[y.Key]; + var temp = max < y.Value; + has |= temp; + shouldPunish[y.Key] = temp; + } + catch (Exception e) + { + Log.ChatError($"GetLess: Missing Point id: {y.Key} [{current.Print()}]/[{maxLimits.Print()}] {e}"); + } + + } + return has; + } + + public static bool IsOneKeyMoreThan (this IDictionary buffer, IDictionary maxLimits) + { + foreach (var y in buffer) + { + try + { + if (y.Value > maxLimits[y.Key]) + { + return true; + } + } + catch (Exception e) + { + Log.ChatError($"IsOneKeyMoreThan: Missing Point id {y.Key}: Buffer:{buffer.Print()}\r\nMax:{maxLimits.Print()} {e}"); + } + } + return false; + } + + public static bool IsOneKeyMoreThanDebug (this IDictionary buffer, IDictionary maxLimits) + { + bool result = false; + foreach (var y in buffer) + { + try + { + if (y.Value > maxLimits[y.Key]) + { + result = true; + Log.ChatError($"Key [{y.Value} / {maxLimits[y.Key]}] ({y.Key})"); + return true; + } + } + catch (Exception e) + { + Log.ChatError($"IsOneKeyMoreThan: Missing Point id {y.Key}: Buffer:{buffer.Print()}\r\nMax:{maxLimits.Print()} {e}"); + } + } + return result; + } + + public static bool IsOneKeyMoreThan (this IDictionary buffer, IDictionary extraBuffer, IDictionary maxLimits) + { + foreach (var y in buffer) + { + var v = extraBuffer.GetOr(y.Key, 0); + if (v <= 0) continue; + + try + { + if (y.Value + v > maxLimits[y.Key]) + { + return true; + } + } + catch (Exception e) + { + Log.ChatError($"IsOneKeyMoreThan2: Missing Point id: {y.Key} {e}"); + } + + } + return false; + } + + public static StringBuilder Append(this StringBuilder sb, IMyPlayer player, IMyFaction faction) { + sb.Append (player.DisplayName); + if (faction != null) { + sb.Append("[").Append(faction.Tag).Append("]"); + } + + return sb; + } + + public static StringBuilder Append(this StringBuilder sb, IMyIdentity player, IMyFaction faction) { + sb.Append (player.DisplayName); + if (faction != null) { + sb.Append("[").Append(faction.Tag).Append("]"); + } + + return sb; + } + + public static K Set(this Dictionary dict, T t, K k) { + K old = default(K); + if (dict.ContainsKey(t)) { + old = dict[t]; + dict.Remove(t); + } + dict.Add(t, k); + return old; + } + + public static int DayOfWeek (DateTime time) //Saturday = 6, Sunday = 7 + { + var utcZero = new DateTime(1970, 1, 1); + if (time < utcZero) return 1; + var d = (y2020 - utcZero).TotalDays; + var dd = (int)d + d%1>0 ? 1 : 0; + dd = dd- (dd / 7)*7 + 4; //1970 was Thursday + if (dd > 7) dd -=7; + return dd; + } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/Timer.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/Timer.cs new file mode 100644 index 00000000..0733b6dc --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/Timer.cs @@ -0,0 +1,73 @@ +namespace MIG.Shared.CSharp { + public class Timer { + protected int time; + + public Timer(int time = 0) { + this.time = time; + } + + public virtual bool tick() { + time--; + return time < 0; + } + + public bool needAction() { + return time < 0; + } + + public int getTime () { + return time; + } + + public void addTime(int time) { + if (this.time < 0) { + this.time = 0; + } + + this.time += time; + } + + public void setTime (int time) { + this.time = time; + } + } + + public class AutoTimer : Timer { + int interval; + + public void setInterval (int interval) { + this.interval = interval; + } + public AutoTimer (int interval, int time = 0) : base(time) { + this.interval = interval; + } + + public int getInterval () { + return interval; + } + + public void setInterval (int interval, int time) { + this.interval = interval; + this.time = time; + } + + public void reset () { + time = interval; + } + + public override bool tick() { + if (base.tick()) { + addTime(interval); + return true; + } else { + return false; + } + } + + public void addTime () { + addTime(interval); + } + + public override string ToString() { return "AutoTimer ["+time + " / " +interval+"]"; } + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Shared/UnitFormatter.cs b/TSTSSESCores/Data/Scripts/Scripts/Shared/UnitFormatter.cs new file mode 100644 index 00000000..a92281d9 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Shared/UnitFormatter.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using VRage.Game; +using VRage.ObjectBuilders; + +namespace MIG.Shared.SE +{ + public static class UnitFormatter + { + private static Dictionary alliases = new Dictionary() { + { MyObjectBuilderType.Parse("MyObjectBuilder_Ingot"), "i/" }, + { MyObjectBuilderType.Parse("MyObjectBuilder_Ore"), "o/" }, + { MyObjectBuilderType.Parse("MyObjectBuilder_Component"), "" } + }; + + public static string toHumanString (this MyDefinitionId id) { + if (alliases.ContainsKey (id.TypeId)) { + return alliases[id.TypeId] +id.SubtypeName; + } else { + return id.TypeId.ToString().Substring (16) + "/"+id.SubtypeName; + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/CoreInfoLcdScript.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/CoreInfoLcdScript.cs new file mode 100644 index 00000000..5f2f51ed --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/CoreInfoLcdScript.cs @@ -0,0 +1,140 @@ +using Sandbox.Game.GameSystems.TextSurfaceScripts; +using Sandbox.ModAPI; +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using VRage.Game.GUI.TextPanel; +using VRage.Game.ModAPI; +using VRageMath; +using ServerMod; +using MIG.Shared.SE; +using IMyTextSurface = Sandbox.ModAPI.Ingame.IMyTextSurface; + + + + +namespace MIG.SpecCores +{ + + [MyTextSurfaceScriptAttribute("CoreInfoLcd", "Core Info Lcd")] + public class CoreInfoLcd : MyTSSCommon + { + + private IMyTextSurface lcd; + private IMyCubeBlock ts_block; + private RectangleF viewport; + private IMyCubeGrid grid; + private StringBuilder RowInfo = new StringBuilder(); + private Color TextColor = Color.White; + + + public CoreInfoLcd(IMyTextSurface lcd_surface, IMyCubeBlock ts_block, Vector2 size) : base(lcd_surface, ts_block, size) + { + this.ts_block = ts_block; + lcd = lcd_surface; + } + + public override void Run() + { + try + { + grid = ts_block.CubeGrid; + Update(); + } + catch (Exception ex) + { + if (ts_block is IMyTextSurfaceProvider) + { + (ts_block as IMyTextSurfaceProvider).GetSurface(0).WriteText("ERROR DESCRIPTION:\n" + ex); + } + } + } + + private void Update() + { + if (SearchSpecCore()) + { + viewport = new RectangleF((lcd.TextureSize - lcd.SurfaceSize) / 2f, lcd.SurfaceSize); + TextColor = lcd.ScriptForegroundColor; + Draw(); + } + } + + private bool SearchSpecCore() + { + bool scready; + try + { + var core = (ISpecBlock)Hooks.GetMainSpecCore(grid); + RowInfo.Clear(); + core.BlockOnAppendingCustomInfo(core.block, RowInfo); + scready = true; + } + catch (Exception e) + { + RowInfo.Clear(); + RowInfo.AppendLine("SpecCore not found!"); + scready = false; + } + return scready; + } + + private void Draw() + { + var frame = lcd.DrawFrame(); + var text = PreparedText(); + var txtboxsize = lcd.MeasureStringInPixels(RowInfo, "Debug", 1f); + + var minside = Math.Min(viewport.Size.X, viewport.Size.Y); + var textscale = MathHelper.Clamp(minside / txtboxsize.Y, 0.4f, 0.75f); + + var charpattern = "["; + var patterstring = new StringBuilder(); + patterstring.Append(charpattern); + var charsize = lcd.MeasureStringInPixels(patterstring, "Debug", textscale); + + var position = new Vector2(viewport.X + 10f, viewport.Y + charsize.Y ); + + + for (int i = 0; i < text.Length; i++) + { + var offset = new Vector2(0, charsize.Y * i); + + var textline = CreateSprite(TextAlignment.LEFT, new Vector2(), text[i], position + offset, TextColor, textscale); + frame.Add(textline); + } + + frame.Dispose(); + + } + + + private string[] PreparedText() + { + var sb = new StringBuilder(); + string[] tmptext = RowInfo.ToString().Split(new[] { "\n" }, StringSplitOptions.None).ToArray(); + for(int i =0; i < tmptext.Length; i++) + { + var line = tmptext[i].Replace("[/Color]", ""); + int startindex = line.IndexOf("]") + 1; + + sb.AppendLine(line.Substring(startindex)); + + } + + return sb.ToString().Split(new[] { "\n" }, StringSplitOptions.None).Skip(1).ToArray(); ; + } + + + private MySprite CreateSprite(TextAlignment textAlignment, Vector2 size, string text_or_type, Vector2 position, Color color, float rotation_or_scale) + { + return new MySprite(SpriteType.TEXT, text_or_type, position, size, color, "Debug", textAlignment, rotation_or_scale); + + } + + + public override ScriptUpdate NeedsUpdate => ScriptUpdate.Update10; + + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/GameBase.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/GameBase.cs new file mode 100644 index 00000000..6e7b42ea --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/GameBase.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Digi; +using VRage.Game.Components; + +namespace MIG.SpecCores +{ + [MySessionComponentDescriptor(MyUpdateOrder.NoUpdate)] + public class GameBase : MySessionComponentBase + { + public static GameBase instance = null; + private readonly List unloadActions = new List(); + public GameBase() + { + instance = this; + } + + protected override void UnloadData() + { + SpaceEngineers.Game.ModAPI.Ingame.IMyTurretControlBlock b = null; + foreach (var x in unloadActions) + { + try + { + x.Invoke(); + } + catch (Exception e) + { + Log.ChatError(e); + } + } + } + + public static void AddUnloadAction(Action a) + { + instance.unloadActions.Add(a); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/LimitsChecker.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/LimitsChecker.cs new file mode 100644 index 00000000..a9232a86 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/LimitsChecker.cs @@ -0,0 +1,663 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using VRage.Game.ModAPI; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.Game.Entities; +using Sandbox.ModAPI; + +namespace MIG.SpecCores +{ + public class LimitsChecker { + public const int TYPE_PRIORITY = -1; + + //TODO + public const int TYPE_PRIORITY_MIN = -2; + public const int TYPE_PRIORITY_MAX = -3; + public const int TYPE_SKIP_CHECKS = -4; + + public const int TYPE_MAX_SMALLGRIDS = -5; + public const int TYPE_MAX_LARGEGRIDS = -6; + public const int TYPE_MAX_GRIDS = -7; + public const int TYPE_MAX_PCU = -8; + public const int TYPE_MAX_BLOCKS = -9; + + public const int TYPE_MIN_SMALLGRIDS = -15; + public const int TYPE_MIN_LARGEGRIDS = -16; + public const int TYPE_MIN_GRIDS = -17; + public const int TYPE_MIN_PCU = -18; + public const int TYPE_MIN_BLOCKS = -19; + + + + + public const float MaxValue = 999999999; + + private static HashSet allSumKeys = new HashSet(); + private static HashSet allLessKeys = new HashSet(); + + private static Limits bufferSum = new Limits(); + private static Limits zeroLimits = new Limits(); + private static Dictionary punishIds = new Dictionary(); + private static List bufferProducers = new List(10); + private static List bufferConsumers = new List(100); + private static List notDrainingConsumers = new List(100); + + private static List gridBuffer1 = new List(); + private static List gridBuffer2 = new List(); + + private static GridGroupInfo GridGroupInfoInstance = new GridGroupInfo(); + + + + private static Limits clientLessTotal = new Limits(); + private static Limits clientLessFound = new Limits(); + private static Limits clientFoundPoints = new Limits(); + private static Limits clientTotalPoints = new Limits(); + + public static void RegisterKey(LimitPoint lp) + { + + if (lp.Id == 0) + { + OriginalSpecCoreSession.AddLoadingError($"Can't register LimitedPoint with id = 0; Info: {lp.Name} {lp.IDD}"); + return; + } + + var key = lp.Id; + clientTotalPoints[key] = 0; + clientFoundPoints[key] = 0; + + zeroLimits[key] = 0; + switch (lp.Behavior) + { + case PointBehavior.SumLessOrEqual: + allSumKeys.Add(key); + punishIds.Add(key, false); + break; + case PointBehavior.LessOrEqual: + allLessKeys.Add(key); + punishIds.Add(key, false); + break; + case PointBehavior.MoreOrEqual: + punishIds.Add(key, false); + break; + + } + } + + public static GridGroupInfo GetGridInfo(IMyTerminalBlock block) + { + block.CubeGrid.GetConnectedGrids(OriginalSpecCoreSession.Instance.Settings.ConnectionType, gridBuffer2, true); + GridGroupInfoInstance.Calculate(gridBuffer2); + return GridGroupInfoInstance; + } + + public static int Compare(float a, float b, bool aa, bool bb) + { + if (aa != bb) + { + if (aa) + { + return -1; + } + else + { + return 1; + } + } + var d = a - b; + return d > 0 ? 1 : d < 0 ? -1 : 0; + } + + + + public static void CheckLimitsInGrid(IMyCubeGrid grid) { + + bool changed = false; + bool changedCore = false; + ISpecBlock selectedSpecBlock = null; + if (grid.isFrozen()) return; + var grids = grid.GetConnectedGrids(OriginalSpecCoreSession.Instance.Settings.ConnectionType, gridBuffer1, true); + + int step = 0; + try + { + + PrepareAndClear(); + step = 1; + + GridGroupInfoInstance.Calculate(grids); + + //var s = new StringBuilder(); + //foreach (var g in grids) + //{ + // s.Append(g.DisplayName + " "); + //} + step = 3; + + Phase_1_CollectInfo(grids); + + step = 4; + + Limits maxLimits; + ISpecBlock producer = null; + bool evenDefaultFailed = false; + step = -44; + if (bufferProducers.Count > 0) + { + step = -51; + bufferProducers.Sort((a, b) => + { + var result = b.Priority.CompareTo(a.Priority); + if (result == 0) + { + result = b.block.EntityId.CompareTo(a.block.EntityId); + } + + return result; + }); + step = -52; + producer = bufferProducers[0]; + maxLimits = producer.GetLimits(); + } + else + { + step = -60; + var type = GridGroupInfoInstance.GetTypeOfGridGroup(grids); + step = -61; + + if (OriginalSpecCoreSession.Instance.Settings.NoSpecCoreSettings != null) + { + maxLimits = OriginalSpecCoreSession.Instance.Settings.NoSpecCoreSettings.GetLimits(type); + } + else + { + maxLimits = new Limits(); + } + + step = -62; + string error; + if (!GridGroupInfoInstance.CanBeApplied(maxLimits, out error)) + { + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError("Cant be applied even no SpecCore settings:" + error); + } + maxLimits = zeroLimits; + } + step = -63; + } + + step = 7; + + Phase_2_TurnOffAllNotMatching(producer, maxLimits, ref changed); + + step = 9; + + Phase_3_TurnOnAllMatching(producer, maxLimits, ref changed); + + step = 11; + + //if (OriginalSpecCoreSession.IsDebug) + //{ + // Log.ChatError($"CheckLimitsInGrid {bufferProducers.Count} | {grids.Count} Limits: {producer?.GetLimits().Print((k,v)=>$"{k}:{v}", separator:" ") ?? "null"}"); + //} + + if (!MyAPIGateway.Utilities.IsDedicated) + { + Phase_4_ClientLimitInfo(producer, grids, evenDefaultFailed); + } + + step = 13; + + + + if (!bufferSum.IsOneKeyMoreThan(maxLimits)) + { + selectedSpecBlock = producer; + + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError("Exceeding limits:" + bufferSum.Print("\n"));//, SharedLogic.NoZeroLimits + Log.ChatError("Max limits:" + maxLimits.Print("\n"));//, SharedLogic.NoZeroLimits + } + + + if (!OriginalSpecCoreSession.RandomPunishment) + { + foreach (var block in bufferConsumers) + { + block.WasInLimitLastTick = true; + } + } + + UpdateCachedCore(grids, producer, ref changed, ref changedCore); + return; + } + + step = 15; + + if (OriginalSpecCoreSession.IsDebug) + { + bufferSum.IsOneKeyMoreThanDebug(maxLimits); + Log.ChatError("Exceeding limits:" + bufferSum.Print("\n"));//, SharedLogic.NoZeroLimits + Log.ChatError("Max limits:" + maxLimits.Print("\n"));//, SharedLogic.NoZeroLimits + Log.ChatError("Producer:" + producer?.block?.DisplayNameText ?? "Null"); + Log.ChatError("Possible Producers:" + bufferProducers.Count); + Log.ChatError("BufferConsumers was fine:" + bufferConsumers.Count((x)=>x.WasInLimitLastTick)); + + + Phase_Debug_CollectInfo(grids); + } + + + + + bufferConsumers.Sort((a, b) => Compare (b.DisableOrder(),a.DisableOrder(), b.WasInLimitLastTick, a.WasInLimitLastTick)); + + step = 17; + + if (MyAPIGateway.Session.IsServer) + { + foreach (var x in bufferConsumers) + { + var consumerLimits = x.GetLimits(); + + if (IsInOverLimit(bufferSum, consumerLimits, maxLimits)) + { + x.Disable(2); + x.WasInLimitLastTick = false; + changed = true; + if (!x.IsDrainingPoints()) + { + bufferSum.MinusIfContains(consumerLimits); + } + } + } + } + + step = 19; + + //Still violating => disable all blocks; + + var punishAll = !bufferSum.GetLess(maxLimits, punishIds); + + if (!punishAll) + { + foreach (var block in bufferConsumers) + { + var consumerLimits = block.GetLimits(); + if (!CheckLessLimitsAndRecordPunishIds(maxLimits, consumerLimits, punishIds)) + { + punishAll = true; + break; + } + } + } + + if (punishAll) + { + //Still violating => disable all blocks; + if (MyAPIGateway.Session.IsServer) + { + foreach (var x in bufferConsumers) + { + x.Punish(punishIds); + } + } + UpdateCachedCore(grids, null, ref changed, ref changedCore); + } + else + { + selectedSpecBlock = producer; + UpdateCachedCore(grids, producer, ref changed, ref changedCore); + } + + step = 21; + } + catch (Exception e) + { + Log.ChatError($"Checker: Step={step} " + e.ToString()); + } + finally + { + if (changed) + { + SendDirtyCustomInfo(grids); + } + + if (changedCore) + { + Hooks.TriggerOnSpecCoreChanged(selectedSpecBlock, grids); + } + } + } + + private static void PrepareAndClear() + { + foreach (var x in allSumKeys) + { + bufferSum[x] = 0; + clientFoundPoints[x] = 0; + clientTotalPoints[x] = 0; + } + + + foreach (var x in allLessKeys) + { + clientLessFound[x] = 0; + clientLessTotal[x] = 0; + } + + bufferProducers.Clear(); + bufferConsumers.Clear(); + notDrainingConsumers.Clear(); + } + + private static void SendDirtyCustomInfo(List grids) + { + foreach (var g in grids) + { + var ship = g.GetShip(); + if (ship != null) + { + foreach (var s in ship.SpecBlocks) + { + var block = s.Key as IMyTerminalBlock; + block?.RefreshDI(); + } + } + } + } + + private static void Phase_1_CollectInfo(List grids) + { + foreach (var g in grids) + { + if ((g as MyCubeGrid).Projector != null) + { + continue; + } + + var ship = g.GetShip(); + if (ship == null) continue; + ship.ResetLimitsTimer(); //Resetting checks + + //Log.ChatError($"CheckLimitsInGrid {g} {ship.LimitedBlocks.Count} | {ship.SpecBlocks.Count}"); + + + + foreach (var x in ship.LimitedBlocks.Values) + { + if (!x.IsDrainingPoints()) + { + notDrainingConsumers.Add(x); + continue; + } + bufferConsumers.Add(x); + bufferSum.PlusIfContains(x.GetLimits()); + } + + if (OriginalSpecCoreSession.IsDebug) + { + //Log.ChatError($"Limited blocks:{bufferConsumers.Count}/{ship.LimitedBlocks.Count} {bufferSum.Print(" ", SharedLogic.NoZeroLimits)}"); + } + + foreach (var x in ship.SpecBlocks.Values) + { + //allBufferProducers.Add(x); + if (x.CanBeApplied(grids, GridGroupInfoInstance)) + { + bufferProducers.Add(x); + } + } + } + } + + private static void Phase_Debug_CollectInfo(List grids) + { + foreach (var g in grids) + { + if ((g as MyCubeGrid).Projector != null) + { + Log.ChatError("IsProjected!"); + continue; + } + + var ship = g.GetShip(); + if (ship == null) + { + Log.ChatError("No ship!"); + continue; + } + ship.ResetLimitsTimer(); //Resetting checks + + foreach (var x in ship.SpecBlocks.Values) + { + //allBufferProducers.Add(x); + if (x.CanBeApplied(grids, GridGroupInfoInstance)) + { + bufferProducers.Add(x); + Log.ChatError($"Applied [{x.block.DisplayName}]"); + x.GetLimitsDebug(); + } + else + { + Log.ChatError($"Cant be applied [{x.block.DisplayName}]"); + } + } + } + } + + private static void Phase_2_TurnOffAllNotMatching(ISpecBlock producer, Limits maxLimits, ref bool changed) + { + + for (var x=0; x grids, bool defaultGGWasFine) + { + //var s = new StringBuilder(); + foreach (var g in grids) + { + if ((g as MyCubeGrid).Projector != null) + { + continue; + } + var ship = g.GetShip(); + if (ship == null) continue; + foreach (var x in ship.LimitedBlocks.Values) + { + var l = x.GetLimits(); + clientLessTotal.MaxIfContains(l); + clientTotalPoints.PlusIfContains(l); + if (x.IsDrainingPoints()) + { + clientLessFound.MaxIfContains(l); + clientFoundPoints.PlusIfContains(l); + } + } + } + + clientFoundPoints.SetValues(clientLessFound); + clientTotalPoints.SetValues(clientLessTotal); + //Log.ChatError("AFTER:" + grids[0].DisplayName + "\n" + clientFoundPoints.Print(" ") + "\n" + clientTotalPoints.Print(" ")); + + + var status = producer != null ? T.ActivationStatus_ErrorOtherCore : + defaultGGWasFine ? T.ActivationStatus_ErrorUsingGridGroupDefault : + T.ActivationStatus_ErrorEvenGridGroupFail; + + foreach (var x in bufferProducers) + { + x.status = status; + } + + if (producer != null) + { + producer.status = T.ActivationStatus_CurrentCore; + } + + foreach (var g in grids) + { + var ship = g.GetShip(); + if (ship == null) continue; + foreach (var x in ship.SpecBlocks.Values) + { + x.FoundLimits.Clear(); + x.TotalLimits.Clear(); + x.FoundLimits.Sum(clientFoundPoints); + x.TotalLimits.Sum(clientTotalPoints); + } + } + } + + public static bool IsInOverLimit (IDictionary buffer, IDictionary consumer, IDictionary maxLimits) + { + foreach (var y in buffer) + { + try + { + var v = consumer.GetOr(y.Key, 0); + if (v == 0) continue; + if (y.Value > maxLimits[y.Key]) + { + return true; + } + } + catch (Exception e) + { + Log.ChatError("IsInOverLimit: " + y.Key + " " + e); + } + + } + return false; + } + + private static bool CheckLessLimits(Limits producer, Limits consumer) + { + foreach (var key in allLessKeys) + { + try + { + float value; + if (consumer.TryGetValue(key, out value) && producer[key] < value) + { + //Log.ChatError($"CheckLessLimits : {producer[key] }: {value}"); + return false; + } + } + catch (Exception e) + { + Log.ChatError($"Key[{key}] not found : " + producer.Print(", ")); + } + + } + + return true; + } + + private static bool CheckLessLimitsAndRecordPunishIds(Limits producer, Limits consumer, Dictionary punishIds) + { + bool isOk = true; + foreach (var key in allLessKeys) + { + try + { + float value; + if (consumer.TryGetValue(key, out value) && producer[key] < value) + { + punishIds[key] = true; + isOk = false; + } + } + catch (Exception e) + { + Log.ChatError($"Key[{key}] not found : " + producer.Print(", ")); + } + + } + + return isOk; + } + + private static void UpdateCachedCore(List grids, ISpecBlock specBlock, ref bool changed, ref bool changedCore) + { + if (specBlock != null) + { + if (specBlock.ValuesChanged) + { + changedCore = true; + specBlock.ValuesChanged = false; + } + } + + + foreach (var g in grids) + { + var ship = g.GetShip(); + if (ship != null) + { + if (ship.CachedCore != specBlock) + { + changed = true; + changedCore = true; + ship.CachedCore = specBlock; + } + } + } + } + + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/GuiIniter2.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/GuiIniter2.cs new file mode 100644 index 00000000..b16afb8d --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/GuiIniter2.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using Digi; +using Sandbox.ModAPI; +using Sandbox.ModAPI.Interfaces.Terminal; +using Scripts.Shared; +using SpaceEngineers.Game.ModAPI; + +namespace MIG.SpecCores +{ + public class SpecBlockGUIIniter2 + { + public bool m_inited = false; + private Action m_init; + + public SpecBlockGUIIniter2(Action Init) + { + m_init = Init; + } + + public void CreateGui(IMyTerminalBlock entity) + { + lock (this) + { + if (m_inited) return; + m_inited = true; + m_init.Invoke(); + } + } + } + + public class SpecBlockGUIIniter : GUIIniter + { + protected override void InitControls() + { + SpecBlock.InitControls(); + } + } + + public class LimitedBlockGUIIniter : GUIIniter + { + protected override void InitControls() + { + LimitedBlock.InitControls(); + } + } + + public class GuiIniter + { + public GUIIniter Initer; + public SpecBlockGUIIniter2 Initer2; + + public GuiIniter(GUIIniter initer, SpecBlockGUIIniter2 initer2) + { + Initer = initer; + Initer2 = initer2; + } + + public void CreateGui(IMyTerminalBlock entity) + { + Initer.CreateGui(entity); + //Initer2.CreateGui(entity); + } + } + + public class GUI + { + public static GuiIniter SpecBlockGui = new GuiIniter( new SpecBlockGUIIniter(), new SpecBlockGUIIniter2(SpecBlock.InitControls)); + public static GuiIniter LimitedBlockGui = new GuiIniter( new LimitedBlockGUIIniter(), new SpecBlockGUIIniter2(LimitedBlock.InitControls)); + + public static void InitSpecBlockGui(GUIClasses classes) + { + var gui = SpecBlockGui.Initer; + InitGui(classes, gui); + GuiControlDuplicateRemover.Init("LimitedBlock", "SpecBlock"); + } + + + public static void InitLimitedBlockGui(GUIClasses classes) + { + var gui = LimitedBlockGui.Initer; + InitGui(classes, gui); + } + + private static void InitGui(GUIClasses classes, GUIIniter gui) + { + if (classes.HasFlag(GUIClasses.Basic)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.EnergyAndProduction)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.RotorsAndPistons)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.ShipControl)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.Tools)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.Weapons)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.Other)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + + if (classes.HasFlag(GUIClasses.Strange)) + { + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + gui.AddType(); + } + } + + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/HudSpecCoreInfo.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/HudSpecCoreInfo.cs new file mode 100644 index 00000000..3b41a763 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/GUI/HudSpecCoreInfo.cs @@ -0,0 +1,117 @@ +using System; +using MIG.SpecCores; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.Utils; + +namespace ServerMod +{ + public class HudSpecCoreInfo : MyHStat + { + private string Error = String.Empty; + + public override void Update() + { + var cu = MyAPIGateway.Session.LocalHumanPlayer?.Controller?.ControlledEntity; + if (cu == null) + { + Error = String.Empty; + CurrentValue = 0; + return; + } + + var cockpit = cu as IMyCubeBlock; + if (cockpit == null) + { + Error = String.Empty; + CurrentValue = 0; + return; + } + + var cubeGrid = cockpit.CubeGrid; + var core = (ISpecBlock)Hooks.GetMainSpecCore(cubeGrid); + if (core == null) + { + Error = T.Translation(OriginalSpecCoreSession.Instance.Settings.HudNoSpecCoreText); + CurrentValue = 2; + return; + } + + if (core.HasOverlimitedBlocks()) + { + Error = T.Translation(OriginalSpecCoreSession.Instance.Settings.HudSpecCoreOverlimitText); + CurrentValue = 1; + return; + } + + + Error = T.Translation(OriginalSpecCoreSession.Instance.Settings.HudSpecCoreActiveText); + Error = String.Format(Error, core.block.DisplayNameText); + CurrentValue = 0.5f; + ValueStringDirty(); + return; + } + + public override string GetId() + { + return "SpecBlock_Errors"; + } + + + public override string ToString() + { + return Error; + } + } + + public abstract class MyHStat : IMyHudStat + { + public virtual float MaxValue => 1f; + public virtual float MinValue => 0.0f; + + private float m_currentValue; + private string m_valueStringCache; + + public abstract void Update(); + public abstract String GetId(); + + public MyStringHash Id { get; protected set; } + + public MyHStat() + { + Id = MyStringHash.GetOrCompute(GetId()); + } + + public float CurrentValue + { + get { return m_currentValue; } + protected set + { + if (m_currentValue == value) + { + return; + } + m_currentValue = value; + ValueStringDirty(); + } + } + + public void ValueStringDirty() + { + m_valueStringCache = null; + } + + public string GetValueString() + { + if (m_valueStringCache == null) + { + m_valueStringCache = ToString(); + } + return m_valueStringCache; + } + + public override string ToString() => string.Format("{0:0}", (float)(CurrentValue * 100.0)); + } + +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Hooks.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Hooks.cs new file mode 100644 index 00000000..7a85c83c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Hooks.cs @@ -0,0 +1,297 @@ +using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.SE; +using Sandbox.Game.Entities; +using Sandbox.ModAPI; +using VRage.Game; +using VRage.Game.ModAPI; +using F = System.Func; +using FF = System.Func; +using A = System.Action; +using C = System.Func; +using U = System.Collections.Generic.List; +using L = System.Collections.Generic.IDictionary; + +namespace MIG.SpecCores +{ + public class Hooks + { + + private static Action registerCustomLimitConsumer = RegisterCustomLimitConsumer; + private static Func getMainSpecCore = GetMainSpecCore; + private static Func getMainSpecCoreBlock = GetMainSpecCoreBlock; + private static Action getSpecCoreLimits = GetSpecCoreLimits; + private static Action getSpecCoreUpgrades = GetSpecCoreUpgrades; + private static Action setSpecCoreUpgrades = SetSpecCoreCustomValues; + + private static Func getSpecCoreBlock = GetSpecCoreBlock; + + private static Func>> getGridBlocksByType = GetGridBlocksByType; + private static Func>> getGridBlocksById = GetGridBlocksById; + + private static Func getBlockSpecCore = GetBlockSpecCore; + private static Func getLimitedBlock = GetLimitedBlock; + private static Func getLimitedBlockBlock = GetLimitedBlockBlock; + + private static Action , float>> registerSpecCoreCurrentPointCustomFx = RegisterSpecCoreCurrentPointCustomFx; + + + private static Action, Dictionary>> addSpecCoreLimitsInterceptor = AddSpecCoreLimitsInterceptor; + private static event Action, Dictionary> SpecCoreLimitsInterceptor = null; + + public static void InvokeLimitsInterceptor(ISpecBlock block, Dictionary st, Dictionary dynam) + { + SpecCoreLimitsInterceptor?.Invoke(block.block, block, st, dynam); + } + + private static Dictionary, float>> SpecCoreCurrentCustom = new Dictionary, float>>(); + + + private static Func, string> CanSpecCoreWork; + + public static Dictionary HookedConsumerInfos = new Dictionary(); + + + public static event Action OnSpecBlockCreated; + public static event Action OnSpecBlockDestroyed; + public static event Action OnLimitedBlockCreated; + public static event Action OnLimitedBlockDestroyed; + + public static event Action> OnSpecCoreChanged; + + public static void TriggerOnSpecCoreChanged(ISpecBlock block, List grids) + { + OnSpecCoreChanged?.Invoke(block, grids); + } + + public static void TriggerOnSpecBlockCreated(ISpecBlock block) + { + OnSpecBlockCreated?.Invoke(block); + } + + public static void TriggerOnSpecBlockDestroyed(ISpecBlock block) + { + OnSpecBlockDestroyed?.Invoke(block); + } + + public static void TriggerOnLimitedBlockCreated(ILimitedBlock block) + { + OnLimitedBlockCreated?.Invoke(block); + } + + public static void TriggerOnLimitedBlockDestroyed(ILimitedBlock block) + { + OnLimitedBlockDestroyed?.Invoke(block); + } + + public static object GetSpecCoreBlock(IMyTerminalBlock block) + { + var grid = block.CubeGrid; + var ship = grid.GetShip(); + if (ship == null) return null; + ISpecBlock specBlock; + ship.SpecBlocks.TryGetValue(block, out specBlock); + return specBlock; + } + + public static Dictionary> GetGridBlocksByType(IMyCubeGrid grid) + { + var ship = grid.GetShip(); + return ship?.BlocksCache; + } + + public static Dictionary> GetGridBlocksById(IMyCubeGrid grid) + { + var ship = grid.GetShip(); + return ship?.BlocksCacheByType; + } + + public static IMyTerminalBlock GetBlockSpecCore(object block) + { + return ((ISpecBlock) block).block; + } + + public static object GetLimitedBlock(IMyTerminalBlock block) + { + var grid = block.CubeGrid; + var ship = grid.GetShip(); + if (ship == null) return null; + ILimitedBlock limitedBlock; + ship.LimitedBlocks.TryGetValue(block, out limitedBlock); + return limitedBlock; + } + + public static IMyTerminalBlock GetLimitedBlockBlock(object block) + { + return ((ILimitedBlock) block).GetBlock(); + } + + + + public static object GetMainSpecCore(IMyCubeGrid grid) + { + Ship ship; + if (OriginalSpecCoreSession.Instance.gridToShip.TryGetValue(grid.EntityId, out ship)) + { + return ship.CachedCore; + } + + return null; + } + + public static IMyTerminalBlock GetMainSpecCoreBlock(IMyCubeGrid grid) + { + Ship ship; + if (OriginalSpecCoreSession.Instance.gridToShip.TryGetValue(grid.EntityId, out ship)) + { + return ship?.CachedCore?.block; + } + + return null; + } + + public static void GetSpecCoreLimits(object specCore, IDictionary dictionary, int mode) + { + var specBlock = specCore as SpecBlock; + if (specBlock == null) return; + + switch (mode) + { + case 1: dictionary.Sum(specBlock.StaticLimits); break; + case 2: dictionary.Sum(specBlock.DynamicLimits); break; + case 3: dictionary.Sum(specBlock.FoundLimits); break; + case 4: dictionary.Sum(specBlock.TotalLimits); break; + case 5: dictionary.Sum(specBlock.Settings.CustomStatic); break; + case 6: dictionary.Sum(specBlock.Settings.CustomDynamic); break; + case 7: dictionary.Sum(specBlock.GetLimits()); break; + } + } + + public static void GetSpecCoreUpgrades(object specCore, List copyTo, int mode) + { + var specBlock = specCore as SpecBlock; + if (specBlock == null) return; + copyTo.AddRange(specBlock.Settings.Upgrades); + } + + public static void GetSpecCoreUpgrades(object specCore, List copyTo) + { + var specBlock = specCore as SpecBlock; + if (specBlock == null) return; + copyTo.AddRange(specBlock.Settings.Upgrades); + } + + public static void SetSpecCoreCustomValues(object specCore, IDictionary staticValues, IDictionary dynamicValues) + { + var specBlock = specCore as SpecBlock; + if (specBlock == null) return; + specBlock.Settings.CustomStatic.Sum(staticValues); + specBlock.Settings.CustomDynamic.Sum(dynamicValues); + specBlock.ApplyUpgrades(); + specBlock.SaveSettings(); + } + + + + /// + /// Must be inited in LoadData of MySessionComponentBase + /// + public static void Init() + { + ModConnection.Init(); + + TorchExtensions.Init(); + + ModConnection.SetValue("MIG.SpecCores.RegisterCustomLimitConsumer", registerCustomLimitConsumer); + ModConnection.SetValue("MIG.SpecCores.GetMainSpecCore", getMainSpecCore); + ModConnection.SetValue("MIG.SpecCores.GetMainSpecCoreBlock", getMainSpecCoreBlock); + ModConnection.SetValue("MIG.SpecCores.GetSpecCoreLimits", getSpecCoreLimits); + ModConnection.SetValue("MIG.SpecCores.GetSpecCoreUpgrades", getSpecCoreUpgrades); + ModConnection.SetValue("MIG.SpecCores.SetSpecCoreCustomValues", setSpecCoreUpgrades); + + ModConnection.SetValue("MIG.SpecCores.GetGridBlocksByType", getGridBlocksByType); + ModConnection.SetValue("MIG.SpecCores.GetGridBlocksById", getGridBlocksById); + + ModConnection.SetValue("MIG.SpecCores.GetSpecCoreBlock", getSpecCoreBlock); + ModConnection.SetValue("MIG.SpecCores.GetBlockSpecCore", getBlockSpecCore); + ModConnection.SetValue("MIG.SpecCores.GetLimitedBlock", getLimitedBlock); + ModConnection.SetValue("MIG.SpecCores.GetLimitedBlockBlock", getLimitedBlockBlock); + + ModConnection.Subscribe("MIG.SpecCores.RegisterCustomLimitConsumer", registerCustomLimitConsumer, (x) => { registerCustomLimitConsumer = x; }); + + ModConnection.SetValue("MIG.SpecCores.RegisterSpecCorePointCustomFx", registerSpecCoreCurrentPointCustomFx); + ModConnection.SetValue("MIG.SpecCores.AddSpecCoreLimitsInterceptor", addSpecCoreLimitsInterceptor); + + ModConnection.Subscribe("MIG.SpecCores.OnSpecBlockCreated", OnSpecBlockCreated, (a) => { OnSpecBlockCreated += a; }); + ModConnection.Subscribe("MIG.SpecCores.OnSpecBlockDestroyed", OnSpecBlockDestroyed, (a) => { OnSpecBlockDestroyed += a; }); + ModConnection.Subscribe("MIG.SpecCores.OnLimitedBlockCreated", OnLimitedBlockCreated, (a) => { OnLimitedBlockCreated += a; }); + ModConnection.Subscribe("MIG.SpecCores.OnLimitedBlockDestroyed", OnLimitedBlockDestroyed, (a) => { OnLimitedBlockDestroyed += a; }); + + ModConnection.Subscribe("MIG.SpecCores.OnSpecBlockChanged", OnSpecCoreChanged, (a) => { OnSpecCoreChanged += a; }); + ModConnection.Subscribe("MIG.SpecCores.CanSpecCoreWork", CanSpecCoreWork, (a) => { CanSpecCoreWork = a; }); + } + + public static void Close() + { + ModConnection.Close(); + } + + public static void RegisterCustomLimitConsumer(string Id, C OnNewConsumerRegistered, A CanWork, FF CheckConditions, F CanBeDisabled, F IsDrainingPoints, A Disable) + { + HookedConsumerInfos[Id] = new HookedLimiterInfo() + { + OnNewConsumerRegistered = OnNewConsumerRegistered, + CanWork = CanWork, + CheckConditions = CheckConditions, + IsDrainingPoints = IsDrainingPoints, + Disable = Disable, + }; + } + + + public static void RegisterSpecCoreCurrentPointCustomFx(int id, Func, float> fx) + { + SpecCoreCurrentCustom[id] = fx; + } + + public static void AddSpecCoreLimitsInterceptor(Action, Dictionary> fx) + { + SpecCoreLimitsInterceptor += fx; + } + + public static string CanBeApplied(ISpecBlock specBlock, List grids) + { + return CanSpecCoreWork?.Invoke(specBlock, grids) ?? null; + } + + public static float GetCurrentPointValueForSpecCore(ISpecBlock specBlock, List grids, LimitPoint lp) + { + var fx = SpecCoreCurrentCustom.GetOr(lp.Id, null); + if (fx == null && OriginalSpecCoreSession.IsDebug) + { + Log.ChatError($"Custom function for PointId={lp.Id} is not found"); + } + return fx?.Invoke(specBlock, grids) ?? 0; + } + + public static float GetMaxPointValueForSpecCore(ISpecBlock specBlock, List grids, LimitPoint lp) + { + var fx = SpecCoreCurrentCustom.GetOr(lp.Id, null); + if (fx == null && OriginalSpecCoreSession.IsDebug) + { + Log.ChatError($"Custom function for PointId={lp.Id} is not found"); + } + return fx?.Invoke(specBlock, grids) ?? 0; + } + } + + public class HookedLimiterInfo + { + public C OnNewConsumerRegistered; + public A CanWork; + public FF CheckConditions; + public F IsDrainingPoints; + public A Disable; + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/HookedLimitedBlock.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/HookedLimitedBlock.cs new file mode 100644 index 00000000..3b8db518 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/HookedLimitedBlock.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using System.Text; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.Definitions; +using Sandbox.ModAPI; +using VRage.ModAPI; + +namespace MIG.SpecCores +{ + public class HookedLimitedBlock : ILimitedBlock { + public IMyTerminalBlock block; + + protected Limits limits; + bool IsCurrentlyOnMainGrid = false; + private LimitedBlockInfo info; + private BlockId blockId; + + public bool WasInLimitLastTick { get; set; } = false; + + public LimitedBlockNetworking Component { get; private set; } + private HookedLimiterInfo hooks; + private object logic; + + public void Destroy() { BlockOnOnMarkForClose(block); } + public bool CheckConditions(ISpecBlock specblock) { return (info.CanWorkWithoutSpecCore || specblock != null) && hooks.CheckConditions(specblock?.block, logic); } + public IMyTerminalBlock GetBlock() { return block; } + public long EntityId() { return block.EntityId; } + public Limits GetLimits() { return limits; } + public bool IsDrainingPoints() { return hooks.IsDrainingPoints(logic); } + public bool MatchesConditions() { return (IsCurrentlyOnMainGrid || info.CanWorkOnSubGrids); } + public bool ShouldBeEnabled() { return Component.Settings.AutoEnable && (Component.Settings.WasDisabledBySpecCore || !Component.Settings.SmartTurnOn); } + + public bool Punish(Dictionary shouldPunish) + { + return true; + //TODO SLIME + } + + public float DisableOrder() { return blockId.DisableOrder; } + + public HookedLimitedBlock(IMyTerminalBlock Entity, LimitedBlockInfo info, HookedLimiterInfo hooks, BlockId blockId) + { + Component = new LimitedBlockNetworking(this, Entity, info.DefaultBlockSettings); + block = Entity; + this.hooks = hooks; + this.info = info; + this.blockId = blockId; + this.limits = info.GetLimits(blockId); + + if (!MyAPIGateway.Session.isTorchServer()) { + block.AppendingCustomInfo += BlockOnAppendingCustomInfo; + block.OnMarkForClose += BlockOnOnMarkForClose; + } + + if (!info.CanWorkOnSubGrids) + { + FrameExecutor.addFrameLogic(new AutoTimer(OriginalSpecCoreSession.Instance.Settings.Timers.CheckBlocksOnSubGridsInterval, OriginalSpecCoreSession.Instance.Settings.Timers.CheckBlocksOnSubGridsInterval), block, Tick); + } + + logic = hooks.OnNewConsumerRegistered(block); + + GUI.LimitedBlockGui.CreateGui(Entity); + } + + + + public void Tick(long frame) + { + IsCurrentlyOnMainGrid = IsOnMainGrid(); + if (!IsCurrentlyOnMainGrid) + { + Disable(1); + } + } + + private bool IsOnMainGrid() + { + var ship = block.CubeGrid.GetShip(); + if (ship != null) + { + foreach (var x in ship.Cockpits) + { + if (x.IsMainControlledCockpit()) return true; + } + } + + return false; + } + + private void BlockOnOnMarkForClose(IMyEntity obj) { + block.OnMarkForClose -= BlockOnOnMarkForClose; + if (!MyAPIGateway.Session.isTorchServer()) { + block.AppendingCustomInfo -= BlockOnAppendingCustomInfo; + } + } + + protected virtual void BlockOnAppendingCustomInfo(IMyTerminalBlock arg1, StringBuilder arg2) { + arg2.AppendLine().AppendT(T.BlockInfo_Header).AppendLine(); + T.GetLimitsInfo(null, arg2, limits, null); + if (!info.CanWorkOnSubGrids) + { + arg2.AppendLine().Append(T.Translation(T.BlockInfo_CantWorkOnSubGrids)).AppendLine(); + } + } + + public void Disable(int reason) + { + if (!MyAPIGateway.Session.IsServer) return; + hooks.Disable(logic); + } + + + public void Enable() + { + if (!MyAPIGateway.Session.IsServer) return; + if (!MatchesConditions()) return; + if (!ShouldBeEnabled()) return; + hooks?.CanWork(logic); + } + + + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/ILimitedBlock.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/ILimitedBlock.cs new file mode 100644 index 00000000..fefb716b --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/ILimitedBlock.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Sandbox.ModAPI; + +namespace MIG.SpecCores +{ + public interface ILimitedBlock + { + bool IsDrainingPoints(); + void Disable(int reason); + long EntityId(); + Limits GetLimits(); + bool CheckConditions(ISpecBlock specblock); + IMyTerminalBlock GetBlock(); + LimitedBlockNetworking Component { get; } + void Enable(); + bool Punish(Dictionary shouldPunish); + float DisableOrder(); + void Destroy(); + bool WasInLimitLastTick { get; set; } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlock.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlock.cs new file mode 100644 index 00000000..f5210931 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlock.cs @@ -0,0 +1,403 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.Definitions; +using Sandbox.Game; +using Sandbox.Game.EntityComponents; +using Sandbox.ModAPI; +using VRage; +using VRage.Game; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRageMath; + +namespace MIG.SpecCores +{ + + public class LimitedBlock : ILimitedBlock { + public IMyTerminalBlock block; + public IMyFunctionalBlock fblock; + + private Limits limits; + private bool IsCurrentlyOnMainGrid = false; + + + private LimitedBlockInfo info; + private BlockId blockId; + private bool IgnoreEvent = false; + private bool CanBePunished = false; + + public LimitedBlockNetworking Component { get; private set; } + public void Destroy() { BlockOnOnMarkForClose(block); } + public bool WasInLimitLastTick { get; set; } = false; + public bool CheckConditions(ISpecBlock specblock) { return info.CanWorkWithoutSpecCore || specblock != null; } + public IMyTerminalBlock GetBlock() { return block; } + public long EntityId() { return block.EntityId; } + public Limits GetLimits() { return limits; } + + + public float DisableOrder() { return blockId.DisableOrder; } + public bool MatchesConditions() { return (IsCurrentlyOnMainGrid || info.CanWorkOnSubGrids); } + public bool ShouldBeEnabled() { return Component.Settings.AutoEnable && (Component.Settings.WasDisabledBySpecCore || !Component.Settings.SmartTurnOn); } + + + public LimitedBlock(IMyTerminalBlock Entity, LimitedBlockInfo info, BlockId blockId) + { + var line = 0; + try + { + block = Entity; + this.blockId = blockId; + fblock = Entity as IMyFunctionalBlock; + line = 1; + + this.info = info; + this.limits = info.GetLimits(blockId); + line = 2; + + + if (!MyAPIGateway.Session.isTorchServer()) { + block.AppendingCustomInfo += BlockOnAppendingCustomInfo; + block.OnMarkForClose += BlockOnOnMarkForClose; + //Entity.RefreshCustomInfo(); //TODO Slime parallel + } + + line = 3; + + if (!info.CanWorkOnSubGrids) + { + FrameExecutor.addFrameLogic(new AutoTimer(OriginalSpecCoreSession.Instance.Settings.Timers.CheckBlocksOnSubGridsInterval, OriginalSpecCoreSession.Instance.Settings.Timers.CheckBlocksOnSubGridsInterval), block, Tick); + } + + line = 4; + if (MyAPIGateway.Session.IsServer) + { + if (fblock != null) + { + fblock.EnabledChanged += OnEnabledChanged; + } + } + + line = 5; + Component = new LimitedBlockNetworking(this, Entity, info.DefaultBlockSettings); + + line = 6; + GUI.LimitedBlockGui.CreateGui(Entity); + line = 7; + } + catch (Exception e) + { + Log.ChatError($"At line {line}", e); + } + + } + + public static void InitControls() where Z : IMyCubeBlock + { + try + { + MyAPIGateway.TerminalControls.CreateCheckbox("SpecCores_AutoEnable", + T.Translation(T.GUI_AutoEnable), + T.Translation(T.GUI_AutoEnableToolTip), + (block) => + { + var s = block?.Component?.Settings; + if (s == null) return false; + return s.AutoEnable; + }, + (block, val) => + { + block.Component.Settings.AutoEnable = val; + block.Component.NotifyAndSave(); + }, + (x)=>x.GetLimitedBlock(), + visible: (x) => + { + var s = x?.Component?.Settings; + if (s == null) return false; + return s.AutoEnableShowGUI || s.AutoEnable; + }); + + MyAPIGateway.TerminalControls.CreateCheckbox("SpecCores_SmartAutoEnable", + T.Translation(T.GUI_SmartAutoEnable), + T.Translation(T.GUI_SmartAutoEnableToolTip), + (block) => + { + var s = block?.Component?.Settings; + if (s == null) return false; + return s.SmartTurnOn; + }, + (block, val) => + { + block.Component.Settings.SmartTurnOn = val; + block.Component.NotifyAndSave(); + }, + (x)=>x.GetLimitedBlock(), + visible: (x) => + { + var s = x?.Component?.Settings; + if (s == null) return false; + return s.SmartTurnOnShowGUI || s.SmartTurnOn; + }); + } + catch (Exception e) + { + Log.ChatError(e); + } + } + + + #region EnableDisableConsuming + + public void Enable() + { + if (!MyAPIGateway.Session.IsServer) return; + + + if (!MatchesConditions()) return; + if (!ShouldBeEnabled()) return; + + + foreach (var behavior in info.Behaviors) + { + if (behavior.EnableBehavior != EnableDisableBehaviorType.None) + { + EnableOrDisable(behavior, behavior.EnableBehavior); + } + } + } + + public void Disable(int reason) + { + if (!MyAPIGateway.Session.IsServer) + { + return; //Clients can't disable blocks + } + + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError($"Disable: {block.DisplayNameText}/{block.CustomName} " + reason); + } + foreach (var behavior in info.Behaviors) + { + if (behavior.DisableBehavior != EnableDisableBehaviorType.None) + { + EnableOrDisable(behavior, behavior.DisableBehavior); + } + } + } + + + public bool Punish(Dictionary shouldPunish) + { + if (!info.CanBePunished) return false; + + int lastId = 0; + try + { + foreach (var behavior in info.Behaviors) + { + if (behavior.PunishBehavior != EnableDisableBehaviorType.None) + { + foreach (var id in behavior.GetPunishedBy()) + { + lastId = id; + if (shouldPunish[id]) + { + if (EnableOrDisable(behavior, behavior.PunishBehavior)) + { + return true; + }; + break; + } + } + } + } + } + catch (Exception e) + { + Log.ChatError($"Punish {this.block.SlimBlock.BlockDefinition.Id} {lastId}"); + } + return false; + } + + public bool IsDrainingPoints() + { + foreach (var behavior in info.Behaviors) + { + if (behavior.ConsumeBehavior != ConsumeBehaviorType.None) + { + if (SharedLogic.IsDrainingPoints(block, fblock, behavior, behavior.ConsumeBehavior)) + { + //if (OriginalSpecCoreSession.IsDebug) + //{ + // Log.ChatError("IsDrainingPoints conditions:" + info.Behaviors.Length + $" : Draining : true : {behavior.ConsumeBehavior} "); + //} + return true; + } + } + } + //if (OriginalSpecCoreSession.IsDebug) + //{ + // Log.ChatError("IsDrainingPoints conditions:" + info.Behaviors.Length + " : Draining : false"); + //} + return false; + } + + private static MyInventory DrainResourcesInventory = new MyInventory(9999999f, + new Vector3(9999999f, 9999999f, 9999999f), MyInventoryFlags.CanReceive | MyInventoryFlags.CanSend); + + public bool EnableOrDisable(Behavior behavior, EnableDisableBehaviorType type) + { + switch (type) + { + case EnableDisableBehaviorType.None: + return false; + + case EnableDisableBehaviorType.Destroy: + block.CubeGrid.RazeBlock(block.Position); + return true; + case EnableDisableBehaviorType.SetArmed: + (block as IMyWarhead).IsArmed = !behavior.Reverse; + return true; + case EnableDisableBehaviorType.SetDrillHarvestMlt: + (block as IMyShipDrill).DrillHarvestMultiplier = behavior.Value1; + return true; + case EnableDisableBehaviorType.SetThrustMlt: + (block as IMyThrust).ThrustMultiplier = behavior.Value1; + return true; + case EnableDisableBehaviorType.SetThrustPowerConsumptionMlt: + (block as IMyThrust).PowerConsumptionMultiplier = behavior.Value1; + return true; + case EnableDisableBehaviorType.SetReactorPowerOutputMlt: + (block as IMyReactor).PowerOutputMultiplier = behavior.Value1; + return true; + case EnableDisableBehaviorType.SetGasGeneratorMlt: + (block as IMyGasGenerator).ProductionCapacityMultiplier = behavior.Value1; + return true; + case EnableDisableBehaviorType.WeldToFunctional: + var critical = (block.SlimBlock.BlockDefinition as MyCubeBlockDefinition).CriticalIntegrityRatio * block.SlimBlock.MaxIntegrity; + + //Log.ChatError($"WeldToFunctional : {block.SlimBlock.Integrity} {critical - behavior.Value2}"); + if (block.SlimBlock.Integrity > critical - behavior.Value2 - block.SlimBlock.MaxIntegrity * behavior.Value3) + { + block.SlimBlock.IncreaseMountLevelToFunctionalState(behavior.Value1, null, block.BuiltBy(), Component.Settings.BeforeDamageShareMode); + } + return true; + case EnableDisableBehaviorType.WeldBy: + block.SlimBlock.IncreaseMountLevelByDesiredRatio(behavior.Value1, behavior.Value2, null, block.BuiltBy(), Component.Settings.BeforeDamageShareMode); + return true; + case EnableDisableBehaviorType.WeldTo: + block.SlimBlock.IncreaseMountLevelToDesiredRatio(behavior.Value1, behavior.Value2, null, block.BuiltBy(), Component.Settings.BeforeDamageShareMode); + return true; + case EnableDisableBehaviorType.SetEnabled: + fblock.Enabled = !behavior.Reverse; + if (behavior.Reverse) + { + Component.Settings.WasDisabledBySpecCore = true; + IgnoreEvent = true; + fblock.Enabled = false; + IgnoreEvent = false; + } + return true; + + case EnableDisableBehaviorType.GrindToFunctional: + case EnableDisableBehaviorType.GrindBy: + case EnableDisableBehaviorType.GrindTo: + if (block.IsFunctional) + { + Component.Settings.BeforeDamageOwnerId = block.OwnerId; + Component.Settings.BeforeDamageShareMode = MyOwnershipShareModeEnum.Faction; //TODO: block.BeforeDamageShareMode; + Component.SaveSettings(); + } + try + { + switch (type) + { + case EnableDisableBehaviorType.GrindToFunctional: + block.SlimBlock.DecreaseMountLevelToFunctionalState(behavior.BoolValue ? DrainResourcesInventory : null, behavior.Value1); + DrainResourcesInventory.Clear(); + return true; + case EnableDisableBehaviorType.GrindBy: + block.SlimBlock.DecreaseMountLevelByDesiredRatio(behavior.Value1, behavior.Value2, behavior.Reverse, behavior.BoolValue ? DrainResourcesInventory : null); + DrainResourcesInventory.Clear(); + return true; + case EnableDisableBehaviorType.GrindTo: + block.SlimBlock.DecreaseMountLevelToDesiredRatio(behavior.Value1, behavior.Value2, behavior.Reverse, behavior.BoolValue ? DrainResourcesInventory : null); + DrainResourcesInventory.Clear(); + return true; + } + } + catch (Exception e) + { + //Skip this exception + } + return true; + + case EnableDisableBehaviorType.SetInventoryMass: + var inv = block.GetInventory() as MyInventory; + var invMass = (double) inv.CurrentMass; + var blockMass = block.Mass; + inv.ExternalMass = (MyFixedPoint)(invMass * behavior.Value1 + blockMass * behavior.Value2 + behavior.Value3); + return true; + + case EnableDisableBehaviorType.CustomLogic: + //TODO SLIME + return true; + } + + return true; + } + + + #endregion + + private void OnEnabledChanged(IMyTerminalBlock obj) + { + if (IgnoreEvent) return; + Component.Settings.WasDisabledBySpecCore = false; + } + + public void Tick(long frame) + { + IsCurrentlyOnMainGrid = IsOnMainGrid(); + if (!IsCurrentlyOnMainGrid) + { + Disable(3); + } + } + + private bool IsOnMainGrid() + { + var ship = block.CubeGrid.GetShip(); + if (ship != null) + { + foreach (var x in ship.Cockpits) + { + if (x.IsMainControlledCockpit()) return true; + } + } + return false; + } + + protected virtual void BlockOnAppendingCustomInfo(IMyTerminalBlock arg1, StringBuilder arg2) { + arg2.AppendLine().AppendT(T.BlockInfo_StaticOnly).AppendLine(); + T.GetLimitsInfo(null, arg2, limits, null); + if (!info.CanWorkOnSubGrids) + { + arg2.AppendLine().Append(T.Translation(T.BlockInfo_CantWorkOnSubGrids)).AppendLine(); + } + } + + private void BlockOnOnMarkForClose(IMyEntity obj) { + block.OnMarkForClose -= BlockOnOnMarkForClose; + if (!MyAPIGateway.Session.isTorchServer()) { + block.AppendingCustomInfo -= BlockOnAppendingCustomInfo; + } + } + + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockNetworking.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockNetworking.cs new file mode 100644 index 00000000..02fbd2f5 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockNetworking.cs @@ -0,0 +1,106 @@ +using System; +using Digi; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public class LimitedBlockNetworking + { + private static Guid Guid = new Guid("82f6e121-550f-4042-a2b8-185ed2a52abd"); + public static Sync Sync; + + public static void Init() + { + Sync = new Sync(17274, (x) => x.Component.Settings, Handler, entityLogicGetter: (id) => + { + var block = id.As(); + if (block == null) return null; + var ship = block.CubeGrid.GetShip(); + if (ship == null) return null; + ILimitedBlock shipSpecBlock; + if (ship.LimitedBlocks.TryGetValue(block, out shipSpecBlock)) + { + return shipSpecBlock; + } + return null; + }); + } + + public LimitedBlockSettings Settings; + public IMyTerminalBlock Block; + public ILimitedBlock LimitedBlock; + public LimitedBlockNetworking(ILimitedBlock limitedBlock, IMyTerminalBlock block, LimitedBlockSettings defaultSettings) + { + Block = block; + LimitedBlock = limitedBlock; + + if (MyAPIGateway.Session.IsServer) + { + if (!block.TryGetStorageData(Guid, out Settings, true)) + { + Settings = new LimitedBlockSettings(defaultSettings); + } + } + else + { + Sync.RequestData(block.EntityId); + } + } + + public static void Handler (ILimitedBlock block, LimitedBlockSettings settings, byte type, ulong userSteamId, bool isFromServer) + { + if (isFromServer && !MyAPIGateway.Session.IsServer) + { + block.Component.Settings = settings; + block.Component.OnSettingsChanged(); + } + else + { + block.Component.ApplyDataFromClient(settings, userSteamId, type); + block.Component.NotifyAndSave(); + block.Component.OnSettingsChanged(); + } + } + + public void OnSettingsChanged() { } + + public void NotifyAndSave(byte type=255, bool forceSave = false) + { + try + { + if (MyAPIGateway.Session.IsServer) + { + Sync.SendMessageToOthers(Block.EntityId, Settings, type: type); + SaveSettings(forceSave); + } + else + { + if (Sync != null) + { + Sync.SendMessageToServer(Block.EntityId, Settings, type: type); + } + } + } + catch (Exception ex) + { + Log.ChatError($"NotifyAndSave {type} Exception {ex} {ex.StackTrace}"); + } + } + + public void SaveSettings(bool forceSave = false) + { + if (MyAPIGateway.Session.IsServer) + { + Block.SetStorageData(Guid, Settings, true); + } + } + + private void ApplyDataFromClient(LimitedBlockSettings blockSettings, ulong playerSteamId, byte command) + { + Settings.AutoEnable = blockSettings.AutoEnable; + Settings.SmartTurnOn = blockSettings.SmartTurnOn; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockSettings.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockSettings.cs new file mode 100644 index 00000000..dbc1f303 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/LimitedBlock/LimitedBlockSettings.cs @@ -0,0 +1,54 @@ +using System.Xml.Serialization; +using ProtoBuf; +using VRage.Game; + +namespace MIG.SpecCores +{ + [ProtoContract] + public class LimitedBlockSettings + { + [XmlAttribute("AutoEnable")] + [ProtoMember(1)] + public bool AutoEnable = true; + + [XmlAttribute("SmartTurnOn")] + [ProtoMember(2)] + public bool SmartTurnOn = true; + + [XmlIgnore()] //State + [ProtoMember(3)] + public bool WasDisabledBySpecCore = true; + + [XmlIgnore()] //State + [ProtoMember(4)] + public long BeforeDamageOwnerId; + + [XmlIgnore()] //State + [ProtoMember(5)] + public MyOwnershipShareModeEnum BeforeDamageShareMode; + + [XmlAttribute("AutoEnableShowGUI")] + [ProtoMember(6)] + public bool AutoEnableShowGUI = true; + + [XmlAttribute("SmartTurnOnShowGUI")] + [ProtoMember(7)] + public bool SmartTurnOnShowGUI = true; + + public LimitedBlockSettings() + { + + } + + public LimitedBlockSettings(LimitedBlockSettings other) + { + AutoEnable = other.AutoEnable; + SmartTurnOn = other.SmartTurnOn; + WasDisabledBySpecCore = other.WasDisabledBySpecCore; + BeforeDamageOwnerId = other.BeforeDamageOwnerId; + BeforeDamageShareMode = other.BeforeDamageShareMode; + AutoEnableShowGUI = other.AutoEnableShowGUI; + SmartTurnOnShowGUI = other.SmartTurnOnShowGUI; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/OriginalSpecCoreSession.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/OriginalSpecCoreSession.cs new file mode 100644 index 00000000..63198d7c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/OriginalSpecCoreSession.cs @@ -0,0 +1,556 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.Definitions; +using Sandbox.ModAPI; +using SharedLib; +using VRage; +using VRage.Game; +using VRage.Game.Components; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRageRender; + +namespace MIG.SpecCores +{ + [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation)] + public class OriginalSpecCoreSession : SessionComponentExternalSettings + { + public const float MaxValue = 99999999; + + public SpecCoreSettings GetDefaultSettings() + { + FrameExecutor.addDelayedLogic(100, (x) => Log.ChatError("Loading default settings")); + return new SpecCoreSettings(); + } + protected override SpecCoreSettings GetDefault() { return GetDefaultSettings(); } + protected override string GetFileName() { return "SpecCoreSettings"; } + + private readonly List addlist = new List(); + private readonly List removelist = new List(); + public readonly Dictionary gridToShip = new Dictionary(); + + public readonly Dictionary Points = new Dictionary(); + public readonly Dictionary PointsByName = new Dictionary(); + + public readonly Dictionary Upgrades = new Dictionary(); + public readonly Dictionary UpgradesByName = new Dictionary(); + + public readonly List LoadingErrors = new List(); + + + + + + public static OriginalSpecCoreSession Instance = null; + public static Dictionary> IdToBlock = new Dictionary>(); + public static bool IsDebug => Instance.Settings.DebugMode; + public static bool RandomPunishment => Instance.Settings.RandomPunishment; + + public override void Init(MyObjectBuilder_SessionComponent sessionComponent) + { + base.Init(sessionComponent); + Instance = this; + SpecBlock.Init(); + LimitedBlockNetworking.Init(); + + int step = 0; + try + { + OnSettingsChanged(ref step); + } + catch (Exception e) + { + AddLoadingError($"Loading settings error at: {step} "+e); + } + + GUI.InitLimitedBlockGui(Settings.LimitedBlocksCanBe); + GUI.InitSpecBlockGui(Settings.SpecBlocksCanBe); + + + + if (LoadingErrors.Count > 0) + { + FrameExecutor.addDelayedLogic(10, (x) => + { + var sb = new StringBuilder(); + foreach (var error in LoadingErrors) + { + sb.AppendLine(error); + } + LoadingErrors.Clear(); + Log.ChatError("Loading XML errors found: \r\n"+sb.ToString()); + }); + } + } + + + private Dictionary> MatcherCache; + private Stopwatch idMatchingStopwatch = new Stopwatch(); + private void GetAllDefinitionIdsMatching(string matcher, List outList) + { + idMatchingStopwatch.Start(); + + if (MatcherCache == null) + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + MatcherCache = new Dictionary>(); + + var allDefinitions = MyDefinitionManager.Static.GetAllDefinitions(); + foreach (var definition in allDefinitions) + { + if (definition is MyCubeBlockDefinition) + { + MatcherCache.GetOrNew(definition.Id.TypeId.ToString().Replace("MyObjectBuilder_", "")).Add(definition.Id.SubtypeName); + } + } + + //AddLoadingError($"Total Defs: {MyDefinitionManager.Static.Loading} {allDefinitions.Count()}"); + + var total = stopwatch.Elapsed.TotalMilliseconds; + stopwatch.Stop(); + } + + + var idMatcher = new BlockIdMatcher(matcher); + foreach (var submatcher in idMatcher.checks) + { + if (submatcher.modeType == BlockIdMatcher.TypeSubtypeMatcher.MODE_EXACT) + { + var list = MatcherCache.GetOr(submatcher.typeString, null); + if (list != null) + { + foreach (var subId in list) + { + if (submatcher.MatchesSubtype(subId)) + { + outList.Add(submatcher.typeString+"/"+subId); + } + } + } + else + { + AddLoadingError($"Not Found blocks for:[{submatcher.typeString}] / [{matcher}]"); + } + } + else + { + foreach (var blockTypeAndSubIds in MatcherCache) + { + if (submatcher.MatchesType(blockTypeAndSubIds.Key)) + { + foreach (var subId in blockTypeAndSubIds.Value) + { + if (submatcher.MatchesSubtype(subId)) + { + outList.Add(blockTypeAndSubIds.Key+"/"+subId); + } + } + } + } + } + } + idMatchingStopwatch.Stop(); + } + + public override void BeforeStart() + { + base.BeforeStart(); + + //Disable Build Info for SpecCores + var t = Other.LoadFirstModFile(GetFileName(), GetDefault); + + if (t.SpecBlocks != null) + { + foreach (var u in t.SpecBlocks) + { + u.AfterDeserialize(); + try + { + var ids = u.BlockIds.ToStrings(); + foreach (var id in ids) + { + var defId = MyDefinitionId.Parse("MyObjectBuilder_" + id); + MyAPIGateway.Utilities.SendModMessage(514062285, new MyTuple("[MIG] SpecCores", "All", defId)); + } + } + catch (Exception e) + { + AddLoadingError($"SpecBlock {u.BlockIds} loading error: {e}"); + } + } + } + } + + private void OnSettingsChanged(ref int step) + { + step = 1; + InitPoints(); + step = 2; + InitUpgrades(); + step = 3; + InitLimitedBlocks(); + step = 4; + //AddLoadingError("Total elapsed:" + idMatchingStopwatch.Elapsed.TotalMilliseconds); + step = 5; + if (Settings.NoSpecCoreSettings != null) + { + Settings.NoSpecCoreSettings.AfterDeserialize(); + } + step = 6; + InitSpecBlocks(); + step = 7; + + Log.CanWriteToChat = Settings.EnableLogs; + step = 8; + + } + + private void InitSpecBlocks() + { + if (Settings.SpecBlocks != null) + { + foreach (var u in Settings.SpecBlocks) + { + u.AfterDeserialize(); + try + { + var ids = u.BlockIds.ToStrings(); + foreach (var id in ids) + { + var defId = MyDefinitionId.Parse("MyObjectBuilder_" + id); + IdToBlock[defId] = new Pair(new BlockId() { Value = u.BlockIds }, u); + } + } + catch (Exception e) + { + AddLoadingError($"SpecBlock {u.BlockIds} loading error: {e}"); + } + } + } + } + private void InitLimitedBlocks() + { + if (Settings.LimitedBlocks != null) + { + var cacheList = new List(); + foreach (var u in Settings.LimitedBlocks) + { + u.AfterDeserialize(); + foreach (var uId in u.BlockIds) + { + try + { + if (uId.Matcher != null) + { + cacheList.Clear(); + GetAllDefinitionIdsMatching(uId.Matcher, cacheList); + + foreach (var subId in cacheList) + { + var id = MyDefinitionId.Parse("MyObjectBuilder_" + subId); + IdToBlock[id] = new Pair(new BlockId(uId, subId), u); + } + + //AddLoadingError($"Found: {cacheList.Count} matching {uId.Matcher}"); + } + + if (uId.Value != null) + { + var strings = uId.Value.Split(new string[] {",", "\r\n", "\n"}, StringSplitOptions.RemoveEmptyEntries); + foreach (var subId in strings) + { + var id = MyDefinitionId.Parse("MyObjectBuilder_" + subId); + IdToBlock[id] = new Pair(new BlockId(uId, subId), u); + } + } + } + catch (Exception e) + { + AddLoadingError($"Was unable to load [{uId.Value}/{uId.Matcher}] LimitedBlock {e}"); + } + } + } + } + } + + private void InitUpgrades() + { + if (Settings.Upgrades != null) + { + foreach (var u in Settings.Upgrades) + { + u.AfterDeserialize(); + + if (u.Name == null) + { + AddLoadingError($"Upgrade with NullName is not supported! [{u}]"); + continue; + } + + if (Upgrades.ContainsKey(u.NId)) + { + AddLoadingError($"Upgrade with NId {u.NId} already exists! [{u}]"); + continue; + } + + if (UpgradesByName.ContainsKey(u.Name)) + { + AddLoadingError($"Upgrade with name {u.Name} already exists! [{u}]"); + continue; + } + Upgrades[u.NId] = u; + UpgradesByName[u.Name] = u; + } + } + } + + /// + /// Must be first as everyone depend on it + /// + private void InitPoints() + { + var pointFormats = new Dictionary(); + if (Settings.PointsFormats != null) + { + foreach (var format in Settings.PointsFormats) + { + pointFormats[format.Id] = format; + //AddLoadingError($"Loading format: {format.Id} {format.Format}"); + } + } + + LimitPointFormat.Init(pointFormats); + + if (Settings.Points != null) + { + foreach (var u in Settings.Points) + { + if (u.FormatId != null) + { + u.Format = pointFormats.GetOr(u.FormatId, null); + //AddLoadingError($"Loading for Limited: {u.FormatId} {u.Format != null}"); + } + + if (u.Format == null) + { + u.Format = LimitPointFormat.GetDefault(u); + //AddLoadingError($"Loading default format for : {u.Name}"); + } + + if (Points.ContainsKey(u.Id)) + { + AddLoadingError($"Point with NID {u.Id} already exists!"); + } + else + { + Points[u.Id] = u; + } + + if (PointsByName.ContainsKey(u.IDD)) + { + AddLoadingError($"Point with ID {u.IDD} already exists!"); + } + else + { + PointsByName[u.IDD] = u; + } + + LimitsChecker.RegisterKey(u); + } + } + + + } + + + public static bool GetLimitedBlock(IMyCubeBlock block, out ILimitedBlock limitedBlock, out ISpecBlock specBlock) + { + limitedBlock = null; + specBlock = null; + var tBlock = block as IMyTerminalBlock; + if (tBlock == null) return false; + + object blockInfo; + Pair blockIdToInfo; + if (!IdToBlock.TryGetValue(block.SlimBlock.BlockDefinition.Id, out blockIdToInfo)) + { + return false; + } + + blockInfo = blockIdToInfo.v; + var consumer = blockInfo as LimitedBlockInfo; + if (consumer != null) + { + limitedBlock = Instance.GetLimitedBlock(tBlock, consumer, blockIdToInfo.k); + if (limitedBlock != null) Hooks.TriggerOnLimitedBlockCreated(limitedBlock); + return limitedBlock != null; + } + + var specBlockInfo = blockInfo as SpecBlockInfo; + if (specBlockInfo != null) + { + specBlock = Instance.GetSpecBlock(tBlock, specBlockInfo); + if (specBlock != null) Hooks.TriggerOnSpecBlockCreated(specBlock); + return specBlock != null; + } + + return false; + } + + public ILimitedBlock GetLimitedBlock(IMyTerminalBlock block, LimitedBlockInfo info, BlockId blockId) + { + if (!string.IsNullOrEmpty(info.CustomLogicId)) + { + HookedLimiterInfo info2; + if (!Hooks.HookedConsumerInfos.TryGetValue(info.CustomLogicId, out info2)) + { + Log.ChatError($"Was unable to get hooked limited block [{info.CustomLogicId}]"); + return null; + } + return new HookedLimitedBlock(block, info, info2, blockId); + } + return new LimitedBlock(block, info, blockId); + } + + public ISpecBlock GetSpecBlock(IMyTerminalBlock block, SpecBlockInfo info) + { + return new SpecBlock(block, info); + } + + public override void LoadData() + { + Hooks.Init(); + Common.Init(); + FrameExecutor.addDelayedLogic(360, (frame) => { Common.SendChatMessage("[MIG] SpecCores v1.2.5 inited!"); }); + MyAPIGateway.Entities.OnEntityAdd += OnEntityAdded; + } + + protected override void UnloadData() + { + Hooks.Close(); + MyAPIGateway.Entities.OnEntityAdd -= OnEntityAdded; + } + + private void OnEntityAdded(IMyEntity ent) + { + try + { + var grid = ent as IMyCubeGrid; + if (grid != null) + { + if (!gridToShip.ContainsKey(grid.EntityId)) + { + var shipGrid = new Ship(grid); + addlist.Add(shipGrid); + grid.OnMarkForClose += OnMarkForClose; + } + } + } + catch (Exception e) + { + Log.ChatError(e); + } + } + + private void OnMarkForClose(IMyEntity ent) + { + if (ent is IMyCubeGrid) + { + removelist.Add(ent.EntityId); + ent.OnMarkForClose -= OnMarkForClose; + } + } + + public override void UpdateBeforeSimulation() + { + base.UpdateBeforeSimulation(); + + foreach (var x in addlist) + { + if (x == null) + { + Log.Error("GB: Add: NULL SHIP"); + continue; + } + + if (x.grid == null) + { + Log.Error("GB: Add: NULL GRID SHIP"); + continue; + } + + if (!gridToShip.ContainsKey(x.grid.EntityId)) + gridToShip.Add(x.grid.EntityId, x); + else + gridToShip[x.grid.EntityId] = x; + } + + addlist.Clear(); + + foreach (var x in removelist) gridToShip.Remove(x); + removelist.Clear(); + + foreach (var x in gridToShip) x.Value.BeforeSimulation(); + + FrameExecutor.Update(); + } + + public float GetTier(BlockId blockId) + { + if (Settings.Tiers != null) + { + foreach (var tier in Settings.Tiers) + { + if (tier.EndsWith != null && blockId.Value.Contains(tier.EndsWith)) return tier.Value; + if (tier.Contains != null && blockId.Value.Contains(tier.Contains)) return tier.Value; + } + } + return Settings.DefaultTier; + } + + public static int GetPointId(string pointName) + { + LimitPoint id; + if (Instance.PointsByName.TryGetValue(pointName, out id)) + { + return id.Id; + } + else + { + AddLoadingError($"Point {pointName} not found:"); + return -1; + } + } + + public static void AddLoadingError(string name) + { + Instance.LoadingErrors.Add(name); + } + + //public override void SaveData() + //{ + // base.SaveData(); + // var s = MyAPIGateway.Utilities.SerializeToXML(Settings); + // Log.ChatError(s); + //} + + + public static void RemoveNonUpgradePoints(Limits maxPoints) + { + foreach (var point in Instance.Points) + { + if (point.Value.Behavior != PointBehavior.UpgradePoint) + { + maxPoints.Remove(point.Key); + } + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Data.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Data.cs new file mode 100644 index 00000000..038f8090 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Data.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using MIG.Shared.CSharp; +using ProtoBuf; + + +namespace MIG.SpecCores +{ + public class Limits : Dictionary + { + public Limits(Limits limits) : base(limits) { } + public Limits() : base() { } + + public Limits Copy() + { + return new Limits(this); + } + } + + [ProtoContract] + public class UpgradableSpecBlockSettings + { + [ProtoMember(1)] + public List Upgrades = new List(); + + [ProtoMember(2)] + public Dictionary CustomStatic = new Dictionary(); + + [ProtoMember(3)] + public Dictionary CustomDynamic = new Dictionary(); + + public override string ToString() + { + return $"Settings {Upgrades.Print()}"; + } + } + + /*public interface SpecBlockUpgrade + { + string Name { get; } + void Upgrade(int times, Limits valuesStatic, Limits valuesDynamic); + } + + public class DefaultSpecBlockUpgrade : SpecBlockUpgrade + { + public string Name { get; } + private Action ToBeApplied; + public DefaultSpecBlockUpgrade(string name, Action toBeApplied) + { + Name = name; + ToBeApplied = toBeApplied; + } + + public void Upgrade(int times, Limits valuesStatic, Limits valuesDynamic) + { + ToBeApplied.Invoke(times, valuesStatic, valuesDynamic); + } + }*/ +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/GridGroupInfo.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/GridGroupInfo.cs new file mode 100644 index 00000000..f625883c --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/GridGroupInfo.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using Digi; +using MIG.Shared.SE; +using Sandbox.Game.Entities; +using VRage.Game; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public class GridGroupInfo + { + public int TotalPCU = 0; + public int TotalBlocks = 0; + public int LargeGrids = 0; + public int SmallGrids = 0; + public void Calculate(List grids) + { + LargeGrids = 0; + SmallGrids = 0; + TotalBlocks = 0; + TotalPCU = 0; + foreach (var x in grids) + { + LargeGrids += x.GridSizeEnum == MyCubeSize.Large ? 1 : 0; + SmallGrids += x.GridSizeEnum == MyCubeSize.Small ? 1 : 0; + TotalPCU += (x as MyCubeGrid).BlocksPCU; + TotalBlocks += (x as MyCubeGrid).BlocksCount; + } + } + + public override string ToString() + { + return $"LargeGrids={LargeGrids} SmallGrids={SmallGrids} TotalPCU={TotalPCU} TotalBlocks={TotalBlocks}"; + } + + public TypeOfGridGroup GetTypeOfGridGroup(List grids) + { + TypeOfGridGroup result = TypeOfGridGroup.None; + foreach (var g in grids) + { + if (g.IsStatic) + { + result |= TypeOfGridGroup.Static; + break; + } + } + result |= LargeGrids > 0 ? TypeOfGridGroup.Large : TypeOfGridGroup.None; + result |= SmallGrids > 0 ? TypeOfGridGroup.Small : TypeOfGridGroup.None; + return result; + } + + public bool CanBeApplied(Limits limits, out string error) + { + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MAX_LARGEGRIDS, LargeGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MAX_SMALLGRIDS, SmallGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MAX_GRIDS, LargeGrids + SmallGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MAX_PCU, TotalPCU, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MAX_BLOCKS, TotalBlocks, out error)) return false; + + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MIN_LARGEGRIDS, LargeGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MIN_SMALLGRIDS, SmallGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MIN_GRIDS, LargeGrids + SmallGrids, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MIN_PCU, TotalPCU, out error)) return false; + if (!CheckSpecCoreLimit(limits, LimitsChecker.TYPE_MIN_BLOCKS, TotalBlocks, out error)) return false; + + return true; + } + + + private bool CheckSpecCoreLimit(Limits limits, int id, float valueToCheck, out string error) + { + error = null; + float value; + + var lp = OriginalSpecCoreSession.Instance.Points.GetOr(id, null); + if (lp == null) + { + return true; + } + + if (limits.TryGetValue(id, out value)) + { + if (lp.Behavior == PointBehavior.LessOrEqual) + { + if (valueToCheck > value) + { + error = lp.ActivationError; + return false; + } + } + else if (lp.Behavior == PointBehavior.MoreOrEqual) + { + if (valueToCheck < value) + { + error = lp.ActivationError; + return false; + } + } + else + { + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError($"Wrong behaviour: {lp.Behavior} for PointId={lp.Id}"); + } + return true; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Lazy.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Lazy.cs new file mode 100644 index 00000000..727d00f9 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Lazy.cs @@ -0,0 +1,49 @@ +using System; +using Sandbox.ModAPI; + +namespace MIG.SpecCores +{ + public class DecayingByFramesLazy : Lazy + { + private long lastUpdate = -1000; + private long framesValid = 0; + + public DecayingByFramesLazy(int framesValid = 0) : base() { this.framesValid = framesValid;} + protected override bool ShouldUpdate(){ return (MyAPIGateway.Session.GameplayFrameCounter - lastUpdate >= framesValid); } + } + + public class Lazy + { + private bool HasCorrectValue = false; + private Func getter; + private T m_value = default(T); + public event Action Changed; + + public T Value + { + get { + if (ShouldUpdate()) + { + var newValue = getter(m_value); + if (!newValue.Equals(m_value)) + { + var oldValue = m_value; + m_value = newValue; + Changed?.Invoke(oldValue, newValue); + } + } + return m_value; + } + } + + + public Lazy() { } + + public void SetGetter(Func getter) + { + this.getter = getter; + } + + protected virtual bool ShouldUpdate() { return !HasCorrectValue; } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Ship.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Ship.cs new file mode 100644 index 00000000..e26a14e3 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Ship.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.Game.Entities; +using VRage.Game; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public class Ship + { + public IMyCubeGrid grid; + public HashSet Cockpits = new HashSet(); + public Dictionary SpecBlocks = new Dictionary(); + public Dictionary LimitedBlocks = new Dictionary(); + public ISpecBlock CachedCore = null; + + public Dictionary> BlocksCache; + public Dictionary> BlocksCacheByType; + + public AutoTimer limitsLastChecked = new AutoTimer(OriginalSpecCoreSession.Instance.Settings.Timers.CheckLimitsInterval, new Random().Next(OriginalSpecCoreSession.Instance.Settings.Timers.CheckLimitsInterval)); + + public Ship(IMyCubeGrid grid) + { + if (OriginalSpecCoreSession.Instance.Settings.Cache?.EnableBlockCache ?? false) + { + BlocksCache = new Dictionary>(); + BlocksCacheByType = new Dictionary>(); + } + + this.grid = grid; + grid.FindBlocks((x)=>Grid_OnBlockAdded(x)); + grid.OnBlockAdded += Grid_OnBlockAdded; + grid.OnBlockRemoved += Grid_OnBlockRemoved; + grid.OnMarkForClose += Grid_OnMarkForClose; + grid.OnGridSplit += Grid_OnGridSplit; + } + + private void Grid_OnMarkForClose(VRage.ModAPI.IMyEntity obj) { + grid.OnBlockAdded -= Grid_OnBlockAdded; + grid.OnBlockRemoved -= Grid_OnBlockRemoved; + grid.OnMarkForClose -= Grid_OnMarkForClose; + grid.OnGridSplit -= Grid_OnGridSplit; + } + + private void Grid_OnBlockRemoved(IMySlimBlock obj) { + onAddedRemoved (obj, false); + } + private void Grid_OnBlockAdded(IMySlimBlock obj) { + onAddedRemoved (obj, true); + } + + private void Grid_OnGridSplit(IMyCubeGrid arg1, IMyCubeGrid arg2) { + //LimitsChecker.OnGridSplit(arg1, arg2); + } + + public void BeforeSimulation() + { + + //RefreshConnections(this); + if (limitsLastChecked.tick()) { + LimitsChecker.CheckLimitsInGrid(grid); + } + } + + internal void onAddedRemoved(IMySlimBlock obj, bool added) { + + var fat = obj.FatBlock; + if (fat != null) { + RegisterUnregisterType(fat, added, Cockpits); + + if (added) + { + ISpecBlock specBlock; + ILimitedBlock limitedBlock; + if (OriginalSpecCoreSession.GetLimitedBlock(fat, out limitedBlock, out specBlock)) + { + if (limitedBlock != null) + { + LimitedBlocks[fat] = limitedBlock; + } + if (specBlock != null) + { + SpecBlocks[fat] = specBlock; + } + } + + if (OriginalSpecCoreSession.Instance.Settings.Cache?.EnableBlockCache ?? false) + { + BlocksCache.GetOrNew(fat.GetType()).Add(fat); + } + + } + else + { + ILimitedBlock block; + ISpecBlock spec; + + + if (LimitedBlocks.TryGetValue(fat, out block)) + { + block.Destroy(); + LimitedBlocks.Remove(fat); + Hooks.TriggerOnLimitedBlockDestroyed(block); + } + + if (SpecBlocks.TryGetValue(fat, out spec)) + { + spec.Destroy(); + SpecBlocks.Remove(fat); + Hooks.TriggerOnSpecBlockDestroyed(spec); + } + + if (OriginalSpecCoreSession.Instance.Settings.Cache?.EnableBlockCache ?? false) + { + BlocksCache.GetOrNew(fat.GetType()).Remove(fat); + } + } + } + } + + private bool RegisterUnregisterType(IMyCubeBlock fat, bool added, ICollection collection) where T : IMyCubeBlock + { + if (fat is T) + { + if (added) collection.Add((T)fat); + else collection.Remove((T)fat); + return true; + } + + return false; + } + + public void ResetLimitsTimer() + { + limitsLastChecked.reset(); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Sugar2.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Sugar2.cs new file mode 100644 index 00000000..161c1cbd --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/Sugar2.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.SE; +using Sandbox.Definitions; +using Sandbox.ModAPI; +using VRage.Game; +using VRage.Game.Entity; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public static class Sugar2 + { + + public static void RefreshDI(this IMyTerminalBlock block) + { + block.SetDetailedInfoDirty(); + block.RefreshCustomInfo(); + } + + + public static List GetConnectedGrids(this IMyCubeGrid grid, GridConnectionType with, List list = null, bool clear = false) { + if (with == GridConnectionType.None) + { + if (list == null) list = new List(); + list.Add(grid); + return list; + } + else + { + if (clear) + { + list?.Clear(); + } + return grid.GetConnectedGrids(with.Get(), list, clear); + } + } + + + + public static void IncreaseMountLevelToFunctionalState(this IMySlimBlock block, float value, IMyInventory outputInventory, long owner = 0, MyOwnershipShareModeEnum shareModeEnum = MyOwnershipShareModeEnum.Faction, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + block.IncreaseMountLevelToDesiredRatio(def.CriticalIntegrityRatio, value, outputInventory, owner, shareModeEnum, def); + } + + public static void IncreaseMountLevelToDesiredRatio(this IMySlimBlock block, float desiredIntegrityRatio, float value, IMyInventory outputInventory, long owner = 0, MyOwnershipShareModeEnum shareModeEnum = MyOwnershipShareModeEnum.Faction, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + float desiredIntegrity = desiredIntegrityRatio * block.MaxIntegrity + value; + float welderAmount = desiredIntegrity - block.Integrity; + + if (welderAmount <= 0f) + return; + + IncreaseMountLevelByDesiredRatio(block, 0f, welderAmount, outputInventory, owner, shareModeEnum, def); + } + + public static void IncreaseMountLevelByDesiredRatio(this IMySlimBlock block, float desiredIntegrityRatio, float value, IMyInventory outputInventory, long owner = 0, MyOwnershipShareModeEnum shareModeEnum = MyOwnershipShareModeEnum.Faction, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + float desiredIntegrity = desiredIntegrityRatio * block.MaxIntegrity + value; + + if (desiredIntegrity <= 0f) + return; + + TorchExtensions.IncreaseMountLevel( + block, + desiredIntegrity / def.IntegrityPointsPerSec, + owner, + (MyInventoryBase)outputInventory, + 0, + false, + shareModeEnum, false); + //block.IncreaseMountLevel(desiredIntegrity / def.IntegrityPointsPerSec, owner, outputInventory, 0, false, shareModeEnum); + } + + + + public static void DecreaseMountLevelToFunctionalState(this IMySlimBlock block, IMyInventory outputInventory, float desiredIntegrityValue, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + block.DecreaseMountLevelToDesiredRatio(def.CriticalIntegrityRatio, desiredIntegrityValue, false, outputInventory); + } + + public static void DecreaseMountLevelToDesiredRatio(this IMySlimBlock block, float desiredIntegrityRatio, float desiredIntegrityValue, bool canDestroy, IMyInventory outputInventory, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + float desiredIntegrity = desiredIntegrityRatio * block.MaxIntegrity; + float grinderAmount = block.Integrity - desiredIntegrity - desiredIntegrityValue; + + DecreaseMountLevelByDesiredRatio(block, 0, grinderAmount, canDestroy, outputInventory, def); + } + + public static void DecreaseMountLevelByDesiredRatio(this IMySlimBlock block, float desiredIntegrityRatio, float desiredIntegrityValue, bool canDestroy, IMyInventory outputInventory, MyCubeBlockDefinition def = null) + { + def = def ?? (block.BlockDefinition as MyCubeBlockDefinition); + + float grinderAmount = desiredIntegrityRatio * block.MaxIntegrity + desiredIntegrityValue; + + if (!canDestroy) + { + grinderAmount = Math.Min(grinderAmount, block.Integrity - 1); + } + + if (grinderAmount <= 0f) + return; + + if (block.FatBlock != null) + grinderAmount *= block.FatBlock.DisassembleRatio; + else + grinderAmount *= def.DisassembleRatio; + + block.DecreaseMountLevel(grinderAmount / def.IntegrityPointsPerSec, outputInventory, useDefaultDeconstructEfficiency: true); + } + + public static GridLinkTypeEnum Get(this GridConnectionType type) + { + switch (type) + { + case GridConnectionType.Electrical: return GridLinkTypeEnum.Electrical; + case GridConnectionType.Logical: return GridLinkTypeEnum.Logical; + case GridConnectionType.Mechanical: return GridLinkTypeEnum.Mechanical; + case GridConnectionType.Physical: return GridLinkTypeEnum.Physical; + case GridConnectionType.NoContactDamage: return GridLinkTypeEnum.NoContactDamage; + default: return GridLinkTypeEnum.Electrical; + } + } + + public static ILimitedBlock GetLimitedBlock(this IMyTerminalBlock t) + { + var ship = t.CubeGrid.GetShip(); + if (ship == null) return null; + ILimitedBlock shipSpecBlock; + if (ship.LimitedBlocks.TryGetValue(t, out shipSpecBlock)) + { + return shipSpecBlock; + } + return null; + } + + public static Limits GetLimits(this UsedPoints[] usedPoints, BlockId blockId) + { + float mlt = blockId.Mlt * blockId.Mlt2; + + Limits result = new Limits(); + if (usedPoints == null) return result; + + foreach (var u in usedPoints) + { + float value; + + if (u.UseTierValue) value = OriginalSpecCoreSession.Instance.GetTier(blockId); + else if (u.UseCustomValue) value = blockId.CustomValue; + else value = u.Amount * (u.UseMlts ? mlt : 1); + + if (!float.IsNaN(u.MinValue)) value = Math.Max(u.MinValue, value); + if (u.RoundLimits) value = (float)Math.Round(value); + result[u.PointId] = value; + } + + return result; + } + + public static Limits GetLimitsForNoSpecBlocks(this UsedPoints[] usedPoints) + { + Limits result = new Limits(); + if (usedPoints == null) return result; + + foreach (var u in usedPoints) + { + result[u.PointId] = u.Amount; + } + + foreach (var kv in OriginalSpecCoreSession.Instance.Points) + { + if (!result.ContainsKey(kv.Key)) + { + result[kv.Key] = kv.Value.DefaultNoSpecCoreValue; + } + } + + return result; + } + + public static Limits GetLimitsForSpecBlocks(this UsedPoints[] defaultValues, UsedPoints[] main) + { + Limits result = new Limits(); + if (defaultValues != null) + { + foreach (var u in defaultValues) + { + result[u.PointId] = u.Amount; + } + } + + if (main != null) + { + foreach (var u in main) + { + result[u.PointId] = u.Amount; + } + } + + foreach (var kv in OriginalSpecCoreSession.Instance.Points) + { + if (!result.ContainsKey(kv.Key)) + { + result[kv.Key] = kv.Value.DefaultSpecCoreValue; + } + } + + return result; + } + + public static ISpecBlock GetSpecBlock(this IMyTerminalBlock t) + { + var ship = t.CubeGrid.GetShip(); + if (ship == null) return null; + ISpecBlock shipSpecBlock; + if (ship.SpecBlocks.TryGetValue(t, out shipSpecBlock)) + { + return shipSpecBlock; + } + return null; + } + + public static SpecBlock GetUpgradableSpecBlock(this IMyTerminalBlock t) + { + return (SpecBlock)GetSpecBlock (t); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/T.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/T.cs new file mode 100644 index 00000000..7d84e02e --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/T.cs @@ -0,0 +1,272 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using VRage; + +namespace MIG.SpecCores +{ + public static class T + { + public static string Infinity = "SpecCores_Infinity"; + + public static string BlockInfo_StatusRow = "SpecCores_BlockInfo_StatusRow";//STATUS: {0}\r\n==============\r\n + public static string BlockInfo_WarningSign = "SpecCores_BlockInfo_WarningSign"; + public static string BlockInfo_NoSpecializations = "SpecCores_BlockInfo_NoSpecializations";//"Block hasn't picked specializations" + public static string BlockInfo_UpgradesHeader = "SpecCores_BlockInfo_UpgradesHeader";//"\r\n==============\r\nUpgrades:" + public static string BlockInfo_UpgradesRow = "SpecCores_BlockInfo_UpgradesRow";//"{0}: {1}" + public static string BlockInfo_StaticOnly = "SpecCores_BlockInfo_StaticOnly";//"Static:" + public static string BlockInfo_DynamicOnly = "SpecCores_BlockInfo_DynamicOnly";//"Dynamic:" + public static string BlockInfo_StaticOrDynamic = "SpecCores_BlockInfo_StaticOrDynamic";//"Static/Dynamic:" + public static string BlockInfo_CantWorkOnSubGrids = "SpecCores_BlockInfo_CantWorkOnSubGrids"; //Cant work on sub grids + public static string BlockInfo_Header = "SpecCores_BlockInfo_Header"; + public static string BlockInfo_UpgradeCosts = "Upgrade Costs"; + + public static string ActivationStatus_CurrentCore = "SpecCores_ActivationStatus_CurrentCore"; + public static string ActivationStatus_ErrorNotWorking = "SpecCores_ActivationStatus_NotWorking"; + public static string ActivationStatus_ErrorNotEnabled = "SpecCores_ActivationStatus_NotEnabled"; + public static string ActivationStatus_ErrorNotFunctional = "SpecCores_ActivationStatus_NotFunctional"; + public static string ActivationStatus_ErrorOtherCore = "SpecCores_ActivationStatus_ErrorOtherCore"; + public static string ActivationStatus_ErrorUsingGridGroupDefault = "SpecCores_ActivationStatus_ErrorUsingGridGroupDefault"; + public static string ActivationStatus_ErrorEvenGridGroupFail = "SpecCores_ActivationStatus_ErrorEvenGridGroupFail"; + + public static string GUI_AutoEnable = "SpecCores_GUI_AutoEnable"; + public static string GUI_AutoEnableToolTip = "SpecCores_GUI_AutoEnableToolTip"; + public static string GUI_SmartAutoEnable = "SpecCores_GUI_SmartAutoEnable"; + public static string GUI_SmartAutoEnableToolTip = "SpecCores_GUI_SmartAutoEnableToolTip"; + + public static string UpgradesList_RowText = "SpecCores_UpgradesList_RowText";//$"{0} {1}/{2}" + + public static string UpgradesList_UpgradeTooltip = "SpecCores_UpgradesList_UpgradeTooltip";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_Row = "SpecCores_UpgradesList_UpgradeTooltip_Row";//$"{0} {1}/{2}" + + public static string UpgradesList_UpgradeTooltip_WouldHavePointsLeft = "SpecCores_UpgradesList_UpgradeTooltip_WouldHavePointsLeft";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_WouldHavePointsLeftZero = "SpecCores_UpgradesList_UpgradeTooltip_WouldHavePointsLeftZero";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_UpgradeCost = "SpecCores_UpgradesList_UpgradeTooltip_UpgradeCost";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_UpgradeCostZero = "SpecCores_UpgradesList_UpgradeTooltip_UpgradeCostZero";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_TotalCost = "SpecCores_UpgradesList_UpgradeTooltip_TotalCost";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_TotalCostZero = "SpecCores_UpgradesList_UpgradeTooltip_TotalCostZero";//$"{0} {1}/{2}" + + + + public static string UpgradesList_UpgradeTooltip_NotEnoughPoints = "SpecCores_UpgradesList_UpgradeTooltip_NotEnoughPoints";//$"{0} {1}/{2}" + public static string UpgradesList_UpgradeTooltip_NotEnoughPoints_Row = "SpecCores_UpgradesList_UpgradeTooltip_NotEnoughPoints_Row";//$"{0}:{1}" + + public static string UpgradesList_UpgradeTooltip_MaxUpgrades = "SpecCores_UpgradesList_UpgradeTooltip_MaxUpgrades";//$"{0} {1}/{2}" + + public static string Button_ApplyRandomUpgrade = "SpecCores_Button_ApplyRandomUpgrade"; + public static string Button_ApplyRandomUpgrade_Tooltip = "SpecCores_Button_ApplyRandomUpgrade_Tooltip"; + public static string Button_ApplyUpgrade = "SpecCores_Button_ApplyUpgrade"; + public static string Button_ApplyUpgrade_Tooltip = "SpecCores_Button_ApplyUpgrade_Tooltip"; + public static string Button_AddUpgrade = "SpecCores_Button_AddUpgrade"; + public static string Button_AddUpgrade_Tooltip = "SpecCores_Button_AddUpgrade_Tooltip"; + public static string Button_RemoveUpgrade = "SpecCores_Button_RemoveUpgrade"; + public static string Button_RemoveUpgrade_Tooltip = "SpecCores_Button_RemoveUpgrade_Tooltip"; + + public static StringBuilder AppendT(this StringBuilder sb, string key, params object[] data) + { + sb.Append(Translation(key, data)); + return sb; + } + + public static StringBuilder AppendT(this StringBuilder sb, string key) + { + sb.Append(Translation(key)); + return sb; + } + + public static string Translation(string key) + { + return MyTexts.GetString(key); + } + + public static string Translation(string key, params object[] data) + { + var s = MyTexts.GetString(key); + return string.Format(s, data); + } + + public static void GetLimitsInfo(Limits foundLimits, StringBuilder sb, Limits maxAvailable, Limits total) + { + int index = sb.Length; + int line = 0; + try + { + line = 1; + var allKeys = new Limits(maxAvailable); + if (foundLimits != null) + { + allKeys.Sum(foundLimits); + } + line = 3; + var keys = new List(allKeys.Keys); + foreach (var kv in OriginalSpecCoreSession.Instance.Points) + { + if (kv.Value == null) + { + throw new Exception("Limit point is null"); + } + + if (kv.Value.Format == null) + { + throw new Exception($"Limit point {kv.Key} format is null"); + } + if (!kv.Value.Format.Visible) keys.Remove(kv.Key); + } + line = 4; + keys.Sort((a, b) => + { + var points = OriginalSpecCoreSession.Instance.Points; + LimitPoint p1, p2; + if (points.TryGetValue(a, out p1) && points.TryGetValue(b, out p2)) + { + var a1 = p1.DisplayOrder; + var a2 = p2.DisplayOrder; + return a1 > a2 ? 1 : a1 < a2 ? -1 : 0; + } + else + { + Log.ChatError("Not found: " + a + " / " + b); + return a > b ? 1 : a < b ? -1 : 0; + } + }); + line = 4; + if (foundLimits != null) + { + foreach (var x in keys) { + var am1 = foundLimits.GetValueOrDefault(x, 0); + var am2 = maxAvailable.GetValueOrDefault(x, 0); + var am3 = total.GetValueOrDefault(x, 0); + + + line = 5; + var limitPoint = OriginalSpecCoreSession.Instance.Points[x]; + LimitPointFormat format = limitPoint.Format; + + if (am1 == 0 && am2 == 0 && am3 == 0 && !format.VisibleIfAllZero) + { + continue; + } + + if (am2 >= OriginalSpecCoreSession.MaxValue && !format.VisibleIfInfinity) + { + continue; + } + line = 6; + + + bool isOverlimit = false; + bool isPossibleOverlimit = false; + + string formatS = Translation(format.Format); + switch (limitPoint.Behavior) + { + case PointBehavior.SumLessOrEqual: + case PointBehavior.LessOrEqual: + if (am1 > am2) + { + formatS = Translation(format.FormatOverlimiting); + isOverlimit = true; + } + else if (am3 > am2) + { + formatS = Translation(format.FormatPossibleOverlimiting); + isPossibleOverlimit = true; + } + break; + case PointBehavior.MoreOrEqual: + if (am1 < am2) + { + formatS = Translation(format.FormatOverlimiting); + isOverlimit = true; + } + else if (am3 < am2) + { + formatS = Translation(format.FormatPossibleOverlimiting); + isPossibleOverlimit = true; + } + break; + case PointBehavior.Property: + break; + } + + line = 7; + + var txt = formatS.Replace((key) => + { + switch (key) + { + case "PointName": return Translation(limitPoint.Name); + case "UnitName": return Translation(limitPoint.UnitName); + case "Max": return FormatNumber(format.NumberFormat, am2); + case "Current": return FormatNumber(format.NumberFormat, am1); + case "Total": return FormatNumber(format.NumberFormat, am3); + default: return Translation(key); + } + }); + + + line = 8; + + + if ((isOverlimit && format.ShowOnTopIfOverlimit) || (isPossibleOverlimit && format.ShowOnTopIfPossibleOverlimit)) + { + var s = txt + "\r\n"; + sb.Insert(index, s); + index += s.Length; + } + else + { + sb.Append(txt); + sb.Append("\r\n"); + } + line = 9; + } + } + else + { + line = 20; + foreach (var x in keys) { + var am2 = maxAvailable.GetValueOrDefault(x, 0); + line = 24; + var limitPoint = OriginalSpecCoreSession.Instance.Points.GetOr(x, null); + if (limitPoint == null) + { + continue; + } + line = 26; + LimitPointFormat format = limitPoint.Format; + if (!format.VisibleIfAllZero && am2 == 0) + { + continue; + } + if (am2 > OriginalSpecCoreSession.MaxValue && !format.VisibleIfInfinity) + { + continue; + } + line = 28; + + sb.Append($"{Translation(limitPoint.Name)}: {FormatNumber(format.NumberFormat, am2)}{Translation(limitPoint.UnitName)}\r\n"); + + line = 30; + } + } + } + catch (Exception e) + { + Log.ChatError($"At line = {line} MAX={maxAvailable?.Print(" ") ?? "NULL" } TOTAL={total?.Print(" ") ?? "NULL"} FOUND={foundLimits?.Print(" ") ?? "NULL"}", e); + } + } + + private static string FormatNumber(string format, float value) + { + if (value >= OriginalSpecCoreSession.MaxValue) + { + return Translation(T.Infinity); + } + return string.Format(format,value); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/XmlData.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/XmlData.cs new file mode 100644 index 00000000..ab4d5360 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/Other/XmlData.cs @@ -0,0 +1,937 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Xml.Serialization; +using Digi; +using MIG.Shared.SE; +using ProtoBuf; +using VRage.Game; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + #region Enums + + [Flags] + public enum TypeOfGridGroup + { + None = 0, + Large = 1, + Small = 2, + Static = 4 + } + + public enum GridConnectionType + { + None, + Logical, + Physical, + NoContactDamage, + Mechanical, + Electrical + } + + [Flags] + public enum GUIClasses + { + Basic = 1, + EnergyAndProduction = 2, + RotorsAndPistons = 4, + ShipControl = 8, + Tools = 16, + Weapons = 32, + Other = 64, + Strange = 128, + + All = Basic | EnergyAndProduction | RotorsAndPistons | ShipControl | Tools | Weapons | Other | Strange + } + + public enum EnableDisableBehaviorType + { + None, + SetEnabled, + WeldToFunctional, + WeldBy, + WeldTo, + SetArmed, + SetInventoryMass, + SetThrustMlt, + SetThrustPowerConsumptionMlt, + SetReactorPowerOutputMlt, + SetGasGeneratorMlt, + Destroy, + SetDrillHarvestMlt, + GrindToFunctional, + GrindBy, + GrindTo, + CustomLogic + } + + public enum ConsumeBehaviorType + { + None, + Always, + IsPlaced, + IsFunctional, + Integrity, + IsEnabled, + IsWorking, + IsSinkingResource, + IsProducingResource, + IsArmed, + DrillHarvestMlt, + InventoryExternalMass, + ThrustMlt, + ThrustPowerConsumptionMlt, + ReactorPowerOutputMlt, + IsBuiltBy, + IsOwnedBy, + CustomLogic + } + + public enum PointBehavior + { + Property, + SumLessOrEqual, + LessOrEqual, + MoreOrEqual, + UpgradePoint + } + + #endregion + + #region Minor + + + public class Cache + { + [XmlAttribute("EnableBlockCache")] + public bool EnableBlockCache; + } + + public class Timers + { + [XmlAttribute("CheckLimitsInterval")] + public int CheckLimitsInterval = 30; + + [XmlAttribute("CheckBlocksOnSubGridsInterval")] + public int CheckBlocksOnSubGridsInterval = 60; + } + + public class Tier + { + [XmlAttribute("Contains")] + public string Contains = null; + + [XmlAttribute("EndsWith")] + public string EndsWith = null; + + [XmlAttribute("Value")] + public int Value = 0; + } + + public class LimitPointFormat + { + private static Dictionary Data = new Dictionary() + { + {"DefaultLessOrEqual", new LimitPointFormat() + { + Format = "[Color=#FF00A000]{PointName}:{Current}<={Max} ({Total}){UnitName}[/Color]", + FormatPossibleOverlimiting ="[Color=#FFA00000]{PointName}:{Current}<={Max} ({Total}){UnitName}{SpecCores_Gui_PossibleOverlimit}[/Color]", + FormatOverlimiting = "[Color=#FFA00000]{PointName}:{Current}<={Max} ({Total}){UnitName}{SpecCores_Gui_Overlimit}[/Color]" + }}, + {"DefaultSumLessOrEqual", new LimitPointFormat() + { + Format = "[Color=#FF00A000]{PointName}:{Current}<={Max} ({Total}){UnitName}[/Color]", + FormatPossibleOverlimiting ="[Color=#FFA00000]{PointName}:{Current}<={Max} ({Total}){UnitName}{SpecCores_Gui_PossibleOverlimit}[/Color]", + FormatOverlimiting = "[Color=#FFA00000]{PointName}:{Current}<={Max} ({Total}){UnitName}{SpecCores_Gui_Overlimit}[/Color]" + }}, + {"DefaultMoreOrEqual", new LimitPointFormat() + { + Format = "[Color=#FF00A000]{PointName}:{Current}>={Max} ({Total}){UnitName}[/Color]", + FormatPossibleOverlimiting ="[Color=#FFA00000]{PointName}:{Current}>={Max} ({Total}){UnitName}{SpecCores_Gui_PossibleOverlimit}[/Color]", + FormatOverlimiting = "[Color=#FFA00000]{PointName}:{Current}>={Max} ({Total}){UnitName}{SpecCores_Gui_Overlimit}[/Color]" + }}, + {"DefaultProperty", new LimitPointFormat() + { + Format = "[Color=#FFA000A0]{PointName}:{Max} {UnitName}[/Color]", + FormatPossibleOverlimiting ="", + FormatOverlimiting = "" + }}, + {"DefaultUpgradePoint", new LimitPointFormat() + { + Format = "[Color=#FF00A0A0]{PointName}:{Max} {UnitName}[/Color]", + FormatPossibleOverlimiting ="", + FormatOverlimiting = "" + }} + }; + + public static void Init(Dictionary data) + { + foreach (var kv in data) + { + Data[kv.Key] = kv.Value; + } + } + + public static LimitPointFormat GetDefault(LimitPoint u) + { + switch (u.Behavior) + { + case PointBehavior.SumLessOrEqual: return Data["DefaultSumLessOrEqual"]; + case PointBehavior.LessOrEqual: return Data["DefaultLessOrEqual"]; + case PointBehavior.MoreOrEqual: return Data["DefaultMoreOrEqual"]; + case PointBehavior.Property: return Data["DefaultProperty"]; + case PointBehavior.UpgradePoint: return Data["DefaultUpgradePoint"]; + default: + throw new Exception($"Unknown default LimitedPointFormat {u.Behavior}"); + } + } + + [XmlAttribute("Id")] + public string Id = ""; + + [XmlAttribute("Format")] + public string Format = ""; + + [XmlAttribute("FormatPossibleOverlimiting")] + public string FormatPossibleOverlimiting = ""; + + [XmlAttribute("FormatOverlimiting")] + public string FormatOverlimiting = ""; + + [XmlAttribute("NumberFormat")] + public string NumberFormat = ""; + + [XmlAttribute("Visible")] + public bool Visible = true; + + [XmlAttribute("ShowOnTopIfOverlimit")] + public bool ShowOnTopIfOverlimit = true; + + [XmlAttribute("ShowOnTopIfPossibleOverlimit")] + public bool ShowOnTopIfPossibleOverlimit = true; + + [XmlAttribute("VisibleIfAllZero")] + public bool VisibleIfAllZero = false; + + [XmlAttribute("VisibleIfInfinity")] + public bool VisibleIfInfinity = true; + } + + #endregion + + #region MainParts + + [ProtoContract] + public class LimitPoint + { + [XmlAttribute("PointId")] + public string IDD = "UnknownId"; + + [XmlAttribute("NId")] + public int Id = 999; + + [XmlAttribute("DefaultSpecCoreValue")] + public float DefaultSpecCoreValue; + + [XmlAttribute("DefaultNoSpecCoreValue")] + public float DefaultNoSpecCoreValue; + + [XmlAttribute("FormatId")] + public string FormatId = null; + + [XmlAttribute("Behavior")] + public PointBehavior Behavior = PointBehavior.SumLessOrEqual; + + [XmlAttribute("IsCustom")] + public bool IsCustom = false; + + [XmlAttribute("Name")] + public string Name = "Unknown"; + + [XmlAttribute("DisplayOrder")] + public float DisplayOrder = 1; + + [XmlAttribute("ActivationError")] + public string ActivationError = "UnknownActivationError"; + + [XmlAttribute("UnitName")] + public string UnitName = ""; + + [XmlIgnore] + public LimitPointFormat Format; + } + + [ProtoContract] + public class UsedPoints + { + [XmlAttribute("PointId")] + public string PointName = "UnknownPointName"; + + [XmlIgnore()] + public int PointId; + + public void AfterDeserialize() + { + PointId = OriginalSpecCoreSession.GetPointId(PointName); + } + + [XmlAttribute("Amount")] + public float Amount; + + [XmlAttribute("UseMlts")] + public bool UseMlts = true; + + [XmlAttribute("UseCustomValue")] + public bool UseCustomValue = false; + + [XmlAttribute("UseTierValue")] + public bool UseTierValue = false; + + [XmlAttribute("MinValue")] + public float MinValue = float.NaN; + + [XmlAttribute("RoundLimits")] + public bool RoundLimits = true; + } + + + public class BlockId + { + [XmlAttribute("Mlt")] + public float Mlt = 1; + + [XmlAttribute("Mlt2")] + public float Mlt2 = 1; + + [XmlAttribute("CustomValue")] + public float CustomValue = 1; + + [XmlAttribute("DisableOrder")] + public float DisableOrder = 1; + + [XmlAttribute("Matcher")] + public string Matcher = null; + + [XmlText] + public string Value; + + public BlockId() + { + } + + public BlockId(BlockId other, string value) + { + Mlt = other.Mlt; + Mlt2 = other.Mlt2; + CustomValue = other.CustomValue; + DisableOrder = other.DisableOrder; + Value = value; + } + } + + public class Behavior + { + [XmlAttribute("DisableBehavior")] + public EnableDisableBehaviorType DisableBehavior; + + [XmlAttribute("PunishBehavior")] + public EnableDisableBehaviorType PunishBehavior; + + [XmlAttribute("ContinuousViolation")] + public EnableDisableBehaviorType ContinuousViolation; + + [XmlAttribute("EnableBehavior")] + public EnableDisableBehaviorType EnableBehavior; + + [XmlAttribute("ConsumeBehavior")] + public ConsumeBehaviorType ConsumeBehavior; + + [XmlAttribute("ProvideBehavior")] + public ConsumeBehaviorType ProvideBehavior; + + [XmlAttribute("Value1")] + public float Value1 = 0; + + [XmlAttribute("Value2")] + public float Value2 = 0; + + [XmlAttribute("Value3")] + public float Value3 = 0; + + [XmlAttribute("Value4")] + public float Value4 = 0; + + [XmlAttribute("Reverse")] + public bool Reverse = false; + + [XmlAttribute("BoolValue")] + public bool BoolValue = false; + + [XmlAttribute("TextValue")] + public string TextValue; + + [XmlAttribute("TextValue2")] + public string TextValue2; + + [XmlAttribute("CustomLogic")] + public string CustomLogic; + + [XmlAttribute("ProvideLimitsError")] + public string ProvideLimitsError = "UnknownError"; + + [XmlAttribute("PunishedBy")] + public string PunishedByPointIds = null; + + [XmlIgnore] + public string[] TextValues; + + [XmlIgnore] + public string[] TextValues2; + + [XmlIgnore] + public long[] TextValues2Longs; + + [XmlIgnore] + private int[] PunishedByCache = null; + + public void AfterDeserialize() + { + TextValues = (TextValue ?? "").ToStrings(",", " "); + TextValues2 = (TextValue2 ?? "").ToStrings(",", " "); + TextValues2Longs = (TextValue2 ?? "").ToLongs(); + + if (PunishedByPointIds == null) + { + PunishedByCache = new int[0]; + } + else if (PunishedByPointIds.ToLower() == "All" || PunishedByPointIds == "*") + { + PunishedByCache = OriginalSpecCoreSession.Instance.Points.Keys.ToArray(); + } + else + { + var list = new List(); + foreach (var s in PunishedByPointIds.ToStrings()) + { + LimitPoint point; + if (!OriginalSpecCoreSession.Instance.PointsByName.TryGetValue(s, out point)) + { + OriginalSpecCoreSession.AddLoadingError($"PunishedBy has wrong PointName {s}"); + } + else + { + list.Add(point.Id); + } + } + + PunishedByCache = list.ToArray(); + } + } + + public int[] GetPunishedBy() + { + return PunishedByCache; + } + + [XmlIgnore] + private MyDefinitionId ResourceDefinitionId; + + [XmlIgnore] + private bool ResourceDefinitionIdInited; + + public MyDefinitionId GetSinkDefinition() + { + if (!ResourceDefinitionIdInited) + { + if (TextValue != null) + { + MyDefinitionId.TryParse("MyObjectBuilder_GasProperties/" + TextValue, out ResourceDefinitionId); + } + + ResourceDefinitionIdInited = true; + } + + return ResourceDefinitionId; + } + + public bool CheckFactionOrUser(IMyFaction faction, long identity) + { + if (faction != null && TextValues != null && TextValues.Length > 0) + { + if (TextValues.Contains(faction.Tag) != Reverse) + { + return true; + } + } + return TextValues2Longs.Contains(identity) != Reverse; + } + } + + + + #endregion + + #region Main + + + public class SpecCoreSettings + { + public Timers Timers = new Timers(); + + public GUIClasses LimitedBlocksCanBe = GUIClasses.Basic | GUIClasses.EnergyAndProduction | GUIClasses.RotorsAndPistons | GUIClasses.Tools | GUIClasses.Weapons; + public GUIClasses SpecBlocksCanBe = GUIClasses.Basic | GUIClasses.ShipControl; + + [XmlArrayItem("Point")] + public LimitPoint[] Points; + + [XmlArrayItem("PointFormat")] + public LimitPointFormat[] PointsFormats; + + public GridConnectionType ConnectionType = GridConnectionType.Physical; + + public SpecBlockInfo[] SpecBlocks; + + + public LimitedBlockInfo[] LimitedBlocks; + + public NoSpecCoreSettings NoSpecCoreSettings; + + + public float DefaultTier = 0; + [XmlArrayItem("Tier")] + public Tier[] Tiers; + + [XmlArrayItem("Upgrade")] + public Upgrade[] Upgrades; + + public bool DebugMode = false; + public bool EnableLogs = true; + public bool RandomPunishment = true; + + public String HudNoSpecCoreText; + public String HudSpecCoreOverlimitText; + public String HudSpecCoreActiveText; + + [XmlElement("Cache")] + public Cache Cache; + + + } + public class NoSpecCoreSettings + { + [XmlArrayItem("Point")] + public UsedPoints[] LargeStatic; + + [XmlArrayItem("Point")] + public UsedPoints[] LargeDynamic; + + [XmlArrayItem("Point")] + public UsedPoints[] SmallStatic; + + [XmlArrayItem("Point")] + public UsedPoints[] SmallDynamic; + + [XmlArrayItem("Point")] + public UsedPoints[] LargeAndSmallStatic; + + [XmlArrayItem("Point")] + public UsedPoints[] LargeAndSmallDynamic; + + [XmlIgnore] + private Dictionary m_limits = new Dictionary(); + + public Limits GetLimits(TypeOfGridGroup mlt) + { + return m_limits[mlt]; + } + + public void AfterDeserialize() + { + foreach (var up in LargeStatic) up.AfterDeserialize(); + foreach (var up in LargeDynamic) up.AfterDeserialize(); + foreach (var up in SmallStatic) up.AfterDeserialize(); + foreach (var up in SmallDynamic) up.AfterDeserialize(); + foreach (var up in LargeAndSmallStatic) up.AfterDeserialize(); + foreach (var up in LargeAndSmallDynamic) up.AfterDeserialize(); + + InitLimits(TypeOfGridGroup.Large); + InitLimits(TypeOfGridGroup.Small); + InitLimits(TypeOfGridGroup.Large | TypeOfGridGroup.Static); + InitLimits(TypeOfGridGroup.Small | TypeOfGridGroup.Static); + InitLimits(TypeOfGridGroup.Large | TypeOfGridGroup.Small); + InitLimits(TypeOfGridGroup.Large | TypeOfGridGroup.Small | TypeOfGridGroup.Static); + } + + private void InitLimits(TypeOfGridGroup mlt) + { + UsedPoints[] pointsArray = null; + switch (mlt) + { + case TypeOfGridGroup.Large: + pointsArray = LargeDynamic; + break; + case TypeOfGridGroup.Static | TypeOfGridGroup.Large: + pointsArray = LargeStatic; + break; + case TypeOfGridGroup.Small: + pointsArray = SmallDynamic; + break; + case TypeOfGridGroup.Static | TypeOfGridGroup.Small: + pointsArray = SmallStatic; + break; + case TypeOfGridGroup.Small | TypeOfGridGroup.Large: + pointsArray = LargeAndSmallDynamic; + break; + case TypeOfGridGroup.Static | TypeOfGridGroup.Small | TypeOfGridGroup.Large: + pointsArray = LargeAndSmallStatic; + break; + } + + m_limits[mlt] = pointsArray.GetLimitsForNoSpecBlocks(); + } + + + } + + public class LimitedBlockInfo + { + [XmlArrayItem("Id")] + public BlockId[] BlockIds; + + [XmlAttribute("CustomLogicId")] + public string CustomLogicId = null; + + [XmlArrayItem("Point")] + public UsedPoints[] UsedPoints; + + [XmlAttribute("CanWorkOnSubGrids")] + public bool CanWorkOnSubGrids = true; + + [XmlAttribute("CanWorkWithoutSpecCore")] + public bool CanWorkWithoutSpecCore = true; + + public LimitedBlockSettings DefaultBlockSettings = new LimitedBlockSettings(); + + [XmlIgnore] + private Dictionary m_limits = new Dictionary(); + + + [XmlArrayItem("Behavior")] + public Behavior[] Behaviors = null; + + [XmlIgnore] + public bool CanBePunished = false; + + public void AfterDeserialize() + { + if (UsedPoints == null || UsedPoints.Length == 0) + { + OriginalSpecCoreSession.AddLoadingError("UsedPoints are null for some LimitedBlockInfo"); + } + else + { + foreach (var usedPoint in UsedPoints) + { + usedPoint.AfterDeserialize(); + } + } + + + Behaviors = Behaviors ?? new Behavior[0]; + + foreach (var behavior in Behaviors) + { + behavior.AfterDeserialize(); + if (behavior.GetPunishedBy().Length != 0) + { + CanBePunished = true; + } + } + } + + public Limits GetLimits(BlockId blockId) + { + Limits result; + if (!m_limits.TryGetValue(blockId, out result)) + { + result = UsedPoints.GetLimits(blockId); + m_limits[blockId] = result; + } + + return result; + } + } + + public class SpecBlockInfo + { + [XmlAttribute("BlockIds")] + public string BlockIds; + + [XmlArrayItem("Behavior")] + public Behavior[] Behaviors = null; + + [XmlArrayItem("Point")] + public UsedPoints[] DefaultStaticAndDynamic; + + [XmlArrayItem("Point")] + public UsedPoints[] DefaultStatic; + + [XmlArrayItem("Point")] + public UsedPoints[] DefaultDynamic; + + [XmlElement("PossibleUpgrades")] + public string PossibleUpgradesString = null; + + + + [XmlIgnore] + public int[] PossibleUpgrades; + + [XmlIgnore] + public Limits DefaultStaticValues; + + [XmlIgnore] + public Limits DefaultDynamicValues; + + public void AfterDeserialize() + { + //Parse PossibleUpgrades + LoadPossibleUpgrades(); + + + LoadPoints(); + + //Parse Behaviors + Behaviors = Behaviors ?? new Behavior[0]; + + foreach (var behavior in Behaviors) + { + behavior.AfterDeserialize(); + } + } + + private void LoadPoints() + { + //Parse Default and Static + if (DefaultStaticAndDynamic != null) foreach (var up in DefaultStaticAndDynamic) up.AfterDeserialize(); + if (DefaultStatic != null) foreach (var up in DefaultStatic) up.AfterDeserialize(); + if (DefaultDynamic != null) foreach (var up in DefaultDynamic) up.AfterDeserialize(); + + DefaultStaticValues = DefaultStaticAndDynamic.GetLimitsForSpecBlocks(DefaultStatic); + DefaultDynamicValues = DefaultStaticAndDynamic.GetLimitsForSpecBlocks(DefaultDynamic); + } + + private void LoadPossibleUpgrades() + { + if (String.IsNullOrEmpty(PossibleUpgradesString)) + { + PossibleUpgrades = new int[0]; + } + else + { + var temp = PossibleUpgradesString.ToStrings(","," ", "\r\n", "\n"); + var lower = PossibleUpgradesString.ToLower(); + var list = new List(); + + foreach (var possibleUpgrade in temp) + { + if (!OriginalSpecCoreSession.Instance.UpgradesByName.ContainsKey(possibleUpgrade) && possibleUpgrade.ToLower() != "except") + { + OriginalSpecCoreSession.AddLoadingError($"Wasn't able to find upgrade with id {possibleUpgrade}"); + } + } + + if (lower.Contains("except")) + { + foreach (var upgrade in OriginalSpecCoreSession.Instance.Upgrades) + { + if (!temp.Contains(upgrade.Value.Name)) + { + list.Add(upgrade.Key); + } + } + } + else + { + foreach (var s in temp) + { + list.Add(OriginalSpecCoreSession.Instance.UpgradesByName[s].NId); + } + } + PossibleUpgrades = list.ToArray(); + } + } + } + + #endregion + + #region Upgrades + + [ProtoContract] + public class Upgrade + { + [XmlAttribute("Name")] + public string Name; + + [XmlAttribute("NId")] + public int NId; + + [XmlAttribute("DisplayName")] + public string DisplayName; + + [XmlArrayItem("Level")] + public UpgradeLevel[] Levels; + + public void AfterDeserialize() + { + + foreach (var ul in Levels) + { + ul.AfterDeserialize(); + } + } + } + + public class Locks + { + [XmlAttribute("LockGroups")] + public string LockGroupsUnparsed = ""; + + [XmlAttribute("AddedHardLocks")] + public string AddHardLocksUnparsed = ""; + + [XmlAttribute("AddedSoftLocks")] + public string AddSoftLocksUnparsed = ""; + + [XmlAttribute("RemovedHardLocks")] + public string RemoveHardLocksUnparsed = ""; + + [XmlAttribute("RemovedSoftLocks")] + public string RemoveSoftLocksUnparsed = ""; + + public override string ToString() + { + return $"{AddHardLocks.Length} {AddSoftLocks.Length} {RemoveHardLocks.Length} {RemoveSoftLocks.Length} {LockGroupsUnparsed} {AddHardLocksUnparsed} {AddSoftLocksUnparsed} {RemoveHardLocksUnparsed} {RemoveSoftLocksUnparsed}"; + } + + [XmlIgnore()] + public string[] LockGroups; + + [XmlIgnore()] + public string[] AddHardLocks; + + [XmlIgnore()] + public string[] AddSoftLocks; + + [XmlIgnore()] + public string[] RemoveHardLocks; + + [XmlIgnore()] + public string[] RemoveSoftLocks; + + public void AfterDeserialize() + { + LockGroups = LockGroupsUnparsed.ToStrings(",", " "); + AddHardLocks = AddHardLocksUnparsed.ToStrings(",", " "); + AddSoftLocks = AddSoftLocksUnparsed.ToStrings(",", " "); + RemoveHardLocks = RemoveHardLocksUnparsed.ToStrings(",", " "); + RemoveSoftLocks = RemoveSoftLocksUnparsed.ToStrings(",", " "); + } + } + + public class UpgradeLevel + { + [XmlArrayItem("Cost")] + public UpgradeCostPart[] Costs; + + [XmlArrayItem("Modificator")] + public AttributeModificator[] Modificators; + + [XmlIgnore()] + public Dictionary TotalUpgradeCost; + + [XmlElement("Locks")] + public Locks Locks = new Locks(); + + public void AfterDeserialize() + { + Locks.AfterDeserialize(); + + if (Modificators != null) + { + foreach (var modificator in Modificators) + { + modificator.AfterDeserialize(); + } + } + + Costs = Costs ?? new UpgradeCostPart[0]; + + var total = new Dictionary(); + foreach (var cost in Costs) + { + cost.AfterDeserialize(); + total.Sum(cost.PointId, cost.Value); + } + TotalUpgradeCost = total; + } + } + + public class AttributeModificator + { + [XmlAttribute("PointId")] + public string PointName; + + [XmlIgnore] + public int PointId; + + [XmlAttribute] + public float SumBefore = 0; + [XmlAttribute] + public float SumStaticBefore = 0; + [XmlAttribute] + public float SumDynamicBefore = 0; + + [XmlAttribute] + public float SumAfter = 0; + [XmlAttribute] + public float SumStaticAfter = 0; + [XmlAttribute] + public float SumDynamicAfter = 0; + + [XmlAttribute] + public float Mlt = 1; + [XmlAttribute] + public float MltStatic = 1; + [XmlAttribute] + public float MltDynamic = 1; + + public void AfterDeserialize() + { + PointId = OriginalSpecCoreSession.GetPointId(PointName); + } + } + + public class UpgradeCostPart + { + [XmlAttribute("PointId")] + public string PointName = "UnknownUpgradeCostId"; + + [XmlIgnore()] + public int PointId; + + [XmlAttribute("Value")] + public float Value; + + public void AfterDeserialize() + { + PointId = OriginalSpecCoreSession.GetPointId(PointName); + } + } + + #endregion + +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SharedLogic.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SharedLogic.cs new file mode 100644 index 00000000..7f046a7e --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SharedLogic.cs @@ -0,0 +1,59 @@ +using System; +using MIG.Shared.SE; +using Sandbox.Game; +using Sandbox.Game.Entities; +using Sandbox.Game.EntityComponents; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public static class SharedLogic + { + public static Func NoZeroLimits = (k, v) => (v != 0); + + public static bool IsDrainingPoints(IMyCubeBlock block, IMyFunctionalBlock fblock, Behavior behavior, ConsumeBehaviorType type) + { + switch (type) + { + case ConsumeBehaviorType.Always: + return true; + case ConsumeBehaviorType.IsPlaced: + return ((MyCubeGrid)block.CubeGrid).GetCubeBlock(block.Min) != null; + case ConsumeBehaviorType.IsEnabled: + return fblock.Enabled != behavior.Reverse; + case ConsumeBehaviorType.IsWorking: + return (fblock.IsWorking) != behavior.Reverse; + case ConsumeBehaviorType.IsSinkingResource: + return (fblock.ResourceSink.CurrentInputByType(behavior.GetSinkDefinition()) > behavior.Value3) == behavior.Reverse; + case ConsumeBehaviorType.IsFunctional: + return block.IsFunctional != behavior.Reverse; + case ConsumeBehaviorType.IsArmed: + return (block as IMyWarhead).IsArmed == behavior.Reverse; + case ConsumeBehaviorType.IsProducingResource: + return (block.Components.Get().CurrentOutputByType(behavior.GetSinkDefinition()) > behavior.Value3) == behavior.Reverse; + case ConsumeBehaviorType.Integrity: + return (block.SlimBlock.Integrity > block.SlimBlock.MaxIntegrity * behavior.Value1 + behavior.Value3) == behavior.Reverse; + + case ConsumeBehaviorType.DrillHarvestMlt: + return ((block as IMyShipDrill).DrillHarvestMultiplier >= behavior.Value1) != behavior.Reverse; + case ConsumeBehaviorType.InventoryExternalMass: + var inv = block.GetInventory() as MyInventory; + return ((double)inv.ExternalMass >= behavior.Value1) != behavior.Reverse; + case ConsumeBehaviorType.ThrustMlt: + return ((block as IMyThrust).ThrustMultiplier >= behavior.Value1) != behavior.Reverse; + case ConsumeBehaviorType.ThrustPowerConsumptionMlt: + return ((block as IMyThrust).PowerConsumptionMultiplier >= behavior.Value1) != behavior.Reverse; + case ConsumeBehaviorType.IsOwnedBy: + return behavior.CheckFactionOrUser(block.OwnerId.PlayerFaction(), block.OwnerId); + case ConsumeBehaviorType.IsBuiltBy: + return behavior.CheckFactionOrUser(block.BuiltBy().PlayerFaction(), block.OwnerId); + case ConsumeBehaviorType.CustomLogic: + //TODO SLIME + return true; + default: + return true; + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/ISpecBlock.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/ISpecBlock.cs new file mode 100644 index 00000000..9a2d0464 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/ISpecBlock.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Digi; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; +using VRage.ModAPI; + +namespace MIG.SpecCores +{ + public abstract class ISpecBlock { + public IMyTerminalBlock block; + public IMyFunctionalBlock fblock; + + public Limits StaticLimits; + public Limits DynamicLimits; + public Limits FoundLimits = new Limits(); + public Limits TotalLimits = new Limits(); + public string status; + public bool ValuesChanged = true; + + public float Priority => GetLimits(false)[LimitsChecker.TYPE_PRIORITY]; + + public ISpecBlock() + { + IsStatic.Changed += IsStaticOnChanged; + } + + private void IsStaticOnChanged(bool arg1, bool arg2) + { + ValuesChanged = true; + } + + public Limits GetLimits(bool hooks = true) + { + var limits = IsStatic.Value ? StaticLimits : DynamicLimits; + if (hooks) + { + Hooks.InvokeLimitsInterceptor(this, StaticLimits, DynamicLimits); + ValuesChanged = true; + } + return new Limits(limits); + } + + public Limits GetLimitsDebug(bool hooks = true) + { + var t = IsStatic.Value ? "StaticLimits" : "DynamicLimits"; + + var limits = IsStatic.Value ? StaticLimits : DynamicLimits; + Log.ChatError($"Producer {this} use {t} : {limits}"); + + if (hooks) + { + Hooks.InvokeLimitsInterceptor(this, StaticLimits, DynamicLimits); + } + return new Limits(limits); + } + + protected DecayingByFramesLazy IsStatic = new DecayingByFramesLazy(6); + + public override string ToString() + { + return $"{block.DisplayName}"; + } + + public bool HasOverlimitedBlocks() + { + var l = GetLimits(); + foreach (var kv in TotalLimits) + { + if (!l.ContainsKey(kv.Key)) + { + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError("No key:" + kv.Key); + } + return true; + } + + if (kv.Value > l[kv.Key]) + { + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError(kv.Key + " " + kv.Value + " " + l[kv.Key]); + } + return true; + } + } + + return false; + } + + public ISpecBlock(IMyTerminalBlock Entity) + { + IsStatic.SetGetter(GetIsStatic); + + block = (Entity as IMyTerminalBlock); + fblock = (Entity as IMyFunctionalBlock); + + if (!MyAPIGateway.Session.isTorchServer()) { + block.AppendingCustomInfo += BlockOnAppendingCustomInfo; + block.OnMarkForClose += BlockOnOnMarkForClose; + } + } + + public void Destroy() + { + BlockOnOnMarkForClose(block); + } + + private static List m_gridBuffer = new List(); + private bool GetIsStatic(bool oldValue) + { + block.CubeGrid.GetConnectedGrids(OriginalSpecCoreSession.Instance.Settings.ConnectionType, m_gridBuffer, true); + foreach (var g in m_gridBuffer) + { + if (g.IsStatic) return true; + } + return false; + } + + protected void SetOptions(Limits staticLimits, Limits dynamicLimits = null) { + this.StaticLimits = staticLimits; + this.DynamicLimits = dynamicLimits; + ValuesChanged = true; + } + + + + public abstract bool CanBeApplied(List grids, GridGroupInfo info); + + private void BlockOnOnMarkForClose(IMyEntity obj) { + block.AppendingCustomInfo -= BlockOnAppendingCustomInfo; + block.OnMarkForClose -= BlockOnOnMarkForClose; + } + + public virtual void BlockOnAppendingCustomInfo(IMyTerminalBlock arg1, StringBuilder arg2) { } + } + + +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlock.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlock.cs new file mode 100644 index 00000000..4aa7aa75 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlock.cs @@ -0,0 +1,551 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public partial class SpecBlock : ISpecBlock + { + private static Guid Guid = new Guid("82f6e121-550f-4042-a2b8-185ed2a52abc"); + + public static Sync Sync; + private const byte APPLY_RANDOM = 125; + private const byte APPLY_SELECTED = 126; + + public UpgradableSpecBlockSettings Settings; + public bool ArrivedDataFromServer = false; + private SpecBlockInfo info; + + + + public static void Init() + { + Sync = new Sync(17273, (x) => x.Settings, Handler, entityLogicGetter: (id) => + { + try + { + var block = id.As(); + if (block == null) return null; + var ship = block.CubeGrid.GetShip(); + if (ship == null) return null; + ISpecBlock shipSpecBlock; + if (ship.SpecBlocks.TryGetValue(block, out shipSpecBlock)) + { + return (SpecBlock)shipSpecBlock; + } + return null; + } + catch (Exception e) + { + Log.ChatError("ELG:" + e); + } + return null; + }); + } + + + + public SpecBlock(IMyTerminalBlock Entity, SpecBlockInfo info) : base(Entity) + { + this.info = info; + Settings = new UpgradableSpecBlockSettings(); + + if (MyAPIGateway.Session.IsServer) + { + if (!Entity.TryGetStorageData(Guid, out Settings, true)) + { + Settings = new UpgradableSpecBlockSettings(); + Settings.Upgrades = new List(); + } + + ApplyUpgrades(); + } + else + { + ApplyUpgrades(); + Sync.RequestData(Entity.EntityId); + FrameExecutor.addDelayedLogic(100, new Resyncher(this)); + } + + + + GUI.SpecBlockGui.CreateGui(Entity); + } + + + private class Resyncher : Action1 + { + public SpecBlock Block; + + public Resyncher(SpecBlock block) + { + Block = block; + } + + public void run(long t) + { + if (Block == null) + { + return; + } + + if (Block.block == null || Block.block.Closed || Block.block.MarkedForClose) + { + return; + } + + if (Block.ArrivedDataFromServer) + { + return; + } + + Sync.RequestData(Block.block.EntityId); + FrameExecutor.addDelayedLogic(100, new Resyncher(Block)); + } + } + + + private void ApplyDataFromClient(UpgradableSpecBlockSettings blockSettings, ulong arg4, byte arg3) + { + //Log.ChatError($"[HandleMessage settings: {blockSettings}"); + if (!CanPickUpgrades()) + { + Log.ChatError($"[{arg4.Identity()?.DisplayName}/{arg4.IdentityId()}] Core already has upgrades: {Settings}: {Settings.Upgrades.Count}"); + return; + } + + if (!block.HasPlayerAccess(arg4.Identity().IdentityId)) + { + Log.ChatError($"[{arg4.Identity()?.DisplayName}/{arg4.IdentityId()}] You dont have access"); + return; + } + + //if (arg3 == APPLY_RANDOM) + //{ + // Settings.Upgrades = GenerateRandomUpgrades(new List(info.PossibleUpgrades), + // info.MaxUpgrades + info.ExtraRandomUpgrades); + //} + //else + + if (arg3 == APPLY_SELECTED) + { + //TODO + //var upgradeCost = GetUpgradeCost(blockSettings.Upgrades); + //if (upgradeCost > info.MaxUpgrades) + //{ + // Log.ChatError($"Exceeding limit {upgradeCost} / {info.MaxUpgrades}"); + // return; + //} + + Settings.Upgrades = blockSettings.Upgrades; + } + } + + public bool CanPickUpgrades() + { + return Settings.Upgrades.Count == 0 && info.PossibleUpgrades.Length > 0; + } + + public void ApplyUpgrades() + { + Limits copyStatic, copyDynamic; + GetUpgradedValues(Settings.Upgrades.SumDuplicates(), out copyStatic, out copyDynamic, true); + SetOptions(copyStatic, copyDynamic); + } + + // ================================================================================ + + private static void Handler(SpecBlock block, UpgradableSpecBlockSettings settings, byte type, ulong userSteamId, bool isFromServer) + { + if (isFromServer && !MyAPIGateway.Session.IsServer) + { + block.Settings = settings; + block.ArrivedDataFromServer = true; + block.OnSettingsChanged(); + } + else + { + block.ApplyDataFromClient(settings, userSteamId, type); + block.NotifyAndSave(); + block.OnSettingsChanged(); + } + } + + public void OnSettingsChanged() + { + ApplyUpgrades(); + } + + protected void NotifyAndSave(byte type = 255, bool forceSave = false) + { + try + { + if (MyAPIGateway.Session.IsServer) + { + Sync.SendMessageToOthers(block.EntityId, Settings, type: type); + SaveSettings(forceSave); + } + else + { + if (Sync != null) + { + Sync.SendMessageToServer(block.EntityId, Settings, type: type); + } + } + } + catch (Exception ex) + { + Log.ChatError($"NotifyAndSave {type} Exception {ex} {ex.StackTrace}"); + } + } + + public void SaveSettings(bool forceSave = false) + { + if (MyAPIGateway.Session.IsServer) + { + block.SetStorageData(Guid, Settings, true); + } + } + + + private void GetUpgradedValues(Dictionary dict, out Limits copyStatic, out Limits copyDynamic, bool hooks) + { + int upgradeId = 0; + int levelNum = 0; + + copyStatic = info.DefaultStaticValues.Copy(); + copyDynamic = info.DefaultDynamicValues.Copy(); + var upgrades = OriginalSpecCoreSession.Instance.Upgrades; + try + { + Dictionary sumStaticBefore = new Dictionary(); + Dictionary sumDynamicBefore = new Dictionary(); + Dictionary sumStaticAfter = new Dictionary(); + Dictionary sumDynamicAfter = new Dictionary(); + Dictionary mltStatic = new Dictionary(); + Dictionary mltDynamic = new Dictionary(); + + foreach (var u in dict) + { + if (u.Value == 0) + { + continue; + } + + if (!upgrades.ContainsKey(u.Key)) + { + Log.ChatError($"Can't find upgrade with id {u.Key}"); + continue; + } + + upgradeId = u.Key; + levelNum = u.Value-1; + + + var upgrade = upgrades[u.Key]; + if (upgrade.Levels.Length < levelNum) + { + continue; + } + + var level = upgrade.Levels[levelNum]; + if (level.Modificators != null) + { + foreach (var am in level.Modificators) + { + sumStaticBefore.Sum(am.PointId, am.SumBefore); + sumStaticBefore.Sum(am.PointId, am.SumStaticBefore); + sumDynamicBefore.Sum(am.PointId, am.SumBefore); + sumDynamicBefore.Sum(am.PointId, am.SumDynamicBefore); + + mltStatic.Mlt(am.PointId, am.Mlt); + mltStatic.Mlt(am.PointId, am.MltStatic); + mltDynamic.Mlt(am.PointId, am.Mlt); + mltDynamic.Mlt(am.PointId, am.MltDynamic); + + sumStaticAfter.Sum(am.PointId, am.SumAfter); + sumStaticAfter.Sum(am.PointId, am.SumStaticAfter); + sumDynamicAfter.Sum(am.PointId, am.SumAfter); + sumDynamicAfter.Sum(am.PointId, am.SumDynamicAfter); + } + } + } + + copyStatic.Sum(sumStaticBefore); + copyDynamic.Sum(sumDynamicBefore); + + copyStatic.Mlt(mltStatic, true); + copyStatic.Mlt(mltDynamic, true); + + copyStatic.Sum(sumStaticAfter); + copyDynamic.Sum(sumDynamicAfter); + + copyDynamic.Sum(Settings.CustomDynamic); + copyStatic.Sum(Settings.CustomStatic); + + if (hooks) + { + Hooks.InvokeLimitsInterceptor(this, copyStatic, copyDynamic); + } + } + catch (Exception e) + { + Log.ChatError($"Not found: {upgradeId} {levelNum} [{upgrades.Print((x,y)=> $"{y.NId} {y.Levels.Length} {y.DisplayName}")}]", e); + } + } + + public string ProvideLimitsError() + { + if (info.Behaviors != null) + { + foreach (var behavior in info.Behaviors) + { + if (behavior.ProvideBehavior != ConsumeBehaviorType.None) + { + if (!SharedLogic.IsDrainingPoints(block, fblock, behavior, behavior.ProvideBehavior)) + { + return behavior.ProvideLimitsError; + } + } + } + } + else + { + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError($"{info.BlockIds} doesn't have Behaviors : Possible error"); + } + } + + + return null; + } + + public override bool CanBeApplied(List grids, GridGroupInfo info) + { + var limits = GetLimits(); + string str; + if (!info.CanBeApplied(limits, out str)) + { + status = str; + return false; + } + + + var provideError = ProvideLimitsError(); + if (provideError != null) + { + //if (OriginalSpecCoreSession.IsDebug) + //{ + // Log.ChatError("ProvideLimitsError: " + T.Translation(provideError)); + //} + status = provideError; + return false; + } + + //if (OriginalSpecCoreSession.IsDebug) + //{ + // Log.ChatError($"Grid core: {block.DisplayNameText ?? "Not a terminal block"} {this.info.Id} Provide limits"); + //} + + foreach (var kv in limits) + { + if (kv.Value < LimitsChecker.MaxValue) + { + var lp = OriginalSpecCoreSession.Instance.Points.GetOr(kv.Key, null); + if (lp == null) continue; + if (!lp.IsCustom) continue; + var result = Hooks.GetCurrentPointValueForSpecCore(this, grids, lp); + + if (result > limits[kv.Key] && lp.Behavior == PointBehavior.LessOrEqual) + { + status = lp.ActivationError; + return false; + } + else if (result < limits[kv.Key] && lp.Behavior == PointBehavior.MoreOrEqual) + { + status = lp.ActivationError; + return false; + } + } + } + + var hookError = Hooks.CanBeApplied(this, grids); + if (hookError != null) + { + status = hookError; + return false; + } + + status = T.Infinity;//T.ActivationStatus_CurrentCore; + return true; + } + + private static List m_appendingCustomInfoBuffer = new List(); + + + + #region Other + + + + private static string GetTooltip(SpecBlock specBlock, int u, Dictionary upgrades,Limits originalStatic, Limits originalDynamic, StringBuilder sb) + { + sb.Clear(); + Limits valuesStaticCopy, valuesDynamicCopy; + + upgrades = new Dictionary(upgrades); + upgrades.Sum(u, 1); + specBlock.GetUpgradedValues(upgrades, out valuesStaticCopy, out valuesDynamicCopy, false); + upgrades.Sum(u, -1); + + + valuesStaticCopy.RemoveDuplicates(originalStatic); + valuesDynamicCopy.RemoveDuplicates(originalDynamic); + + + var duplicates = valuesDynamicCopy.GetDuplicates(valuesStaticCopy); + if (duplicates.Count > 0) + { + sb.AppendLine(T.Translation(T.BlockInfo_StaticOrDynamic)); + T.GetLimitsInfo(null, sb, duplicates, null); + sb.AppendLine(); + } + + valuesStaticCopy.RemoveDuplicates(duplicates); + valuesDynamicCopy.RemoveDuplicates(duplicates); + + if (valuesStaticCopy.Count > 0) + { + sb.AppendLine(T.Translation(T.BlockInfo_StaticOnly)); + T.GetLimitsInfo(null, sb, valuesStaticCopy, null); + sb.AppendLine(); + } + if (valuesDynamicCopy.Count > 0) + { + sb.AppendLine(T.Translation(T.BlockInfo_DynamicOnly)); + T.GetLimitsInfo(null, sb, valuesDynamicCopy, null); + sb.AppendLine(); + } + + return sb.ToString(); + } + + private static void UpdateControls(SpecBlock x) + { + try + { + var v = x.block.ShowInToolbarConfig; + x.block.ShowInToolbarConfig = !v; + x.block.ShowInToolbarConfig = v; + } + catch (Exception e) + { + + } + } + + #endregion + + #region GUI + + public override void BlockOnAppendingCustomInfo(IMyTerminalBlock arg1, StringBuilder arg2) + { + try + { + if (arg1 == null || arg1.CubeGrid == null || arg1.MarkedForClose || arg1.CubeGrid.MarkedForClose) + return; + + if (arg1.CubeGrid.GetShip() == null) + { + //Log.ChatError($"BlockOnAppendingCustomInfo: Ship is null WTF? {arg1.CubeGrid.WorldMatrix.Translation} / {arg1.CubeGrid.Closed} {arg1.DisplayName}/{arg1.CustomName} {arg1.Closed}"); + return; + } + + var upgrades = (CanPickUpgrades() ? AddedUpgrades : Settings.Upgrades).SumDuplicates(); + + Limits copyStatic, copyDynamic; + GetUpgradedValues(upgrades, out copyStatic, out copyDynamic, true); + + + arg2.AppendLine().AppendT(T.BlockInfo_StatusRow, T.Translation(status)); + + if (CanPickUpgrades()) + { + arg2.AppendLine(T.Translation(T.BlockInfo_NoSpecializations)); + } + + var grids = block.CubeGrid.GetConnectedGrids(OriginalSpecCoreSession.Instance.Settings.ConnectionType, m_appendingCustomInfoBuffer, true); + + var limits = IsStatic.Value ? copyStatic : copyDynamic; + foreach (var kv in limits) + { + var lp = OriginalSpecCoreSession.Instance.Points.GetOr(kv.Key, null); + if (lp == null) continue; + if (lp.IsCustom) + { + var result = Hooks.GetCurrentPointValueForSpecCore(this, grids, lp); + FoundLimits[kv.Key] = result; + } + } + + var gridInfo = LimitsChecker.GetGridInfo(arg1); + var lps = OriginalSpecCoreSession.Instance.Points; + if (lps.ContainsKey(LimitsChecker.TYPE_MAX_SMALLGRIDS)) FoundLimits[LimitsChecker.TYPE_MAX_SMALLGRIDS] = gridInfo.SmallGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MAX_LARGEGRIDS)) FoundLimits[LimitsChecker.TYPE_MAX_LARGEGRIDS] = gridInfo.LargeGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MAX_GRIDS)) FoundLimits[LimitsChecker.TYPE_MAX_GRIDS] = gridInfo.LargeGrids + gridInfo.SmallGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MAX_PCU)) FoundLimits[LimitsChecker.TYPE_MAX_PCU] = gridInfo.TotalPCU; + if (lps.ContainsKey(LimitsChecker.TYPE_MAX_BLOCKS)) FoundLimits[LimitsChecker.TYPE_MAX_BLOCKS] = gridInfo.TotalBlocks; + + if (lps.ContainsKey(LimitsChecker.TYPE_MIN_SMALLGRIDS)) FoundLimits[LimitsChecker.TYPE_MIN_SMALLGRIDS] = gridInfo.SmallGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MIN_LARGEGRIDS)) FoundLimits[LimitsChecker.TYPE_MIN_LARGEGRIDS] = gridInfo.LargeGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MIN_GRIDS)) FoundLimits[LimitsChecker.TYPE_MIN_GRIDS] = gridInfo.LargeGrids + gridInfo.SmallGrids; + if (lps.ContainsKey(LimitsChecker.TYPE_MIN_PCU)) FoundLimits[LimitsChecker.TYPE_MIN_PCU] = gridInfo.TotalPCU; + if (lps.ContainsKey(LimitsChecker.TYPE_MIN_BLOCKS)) FoundLimits[LimitsChecker.TYPE_MIN_BLOCKS] = gridInfo.TotalBlocks; + + T.GetLimitsInfo(FoundLimits, arg2, limits, TotalLimits); + + + if (info.PossibleUpgrades.Length > 0) + { + var ucount = 0; + foreach (var u in upgrades) + { + ucount += u.Value; + } + arg2.AppendT(T.BlockInfo_UpgradesHeader, ucount); + + foreach (var u in upgrades) + { + Upgrade up; + if (OriginalSpecCoreSession.Instance.Upgrades.TryGetValue(u.Key, out up)) + { + var txt = T.Translation(T.BlockInfo_UpgradesRow, T.Translation(up.DisplayName), u.Value); + arg2.Append(txt).AppendLine(); + } + else + { + arg2.Append($"Unknown upgrade {u.Key}").Append(": ").Append(u.Value+1).AppendLine(); + } + } + } + } + catch (Exception e) + { + arg2.Append(e.ToString()); + } + + } + + + + + + #endregion + } +} diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlockGui.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlockGui.cs new file mode 100644 index 00000000..6014ecdc --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/SpecBlockGui.cs @@ -0,0 +1,410 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using Sandbox.ModAPI.Interfaces.Terminal; +using VRage.Game.ModAPI; +using VRage.ModAPI; +using VRage.Utils; +using Exception = System.Exception; + +namespace MIG.SpecCores +{ + public partial class SpecBlock + { + /// + /// Отвечает за выбранные элементы в списке. + /// + private List SelectedUpgrades = new List(); + + /// + /// Текущие тестовые настройки + /// + private List AddedUpgrades = new List(); + + private static void UpgradesListBoxItemSelected(IMyTerminalBlock a, List b) + { + var specBlock = (SpecBlock)a.GetSpecBlock(); + specBlock.SelectedUpgrades.Clear(); + foreach (var item in b) + { + specBlock.SelectedUpgrades.Add((int)item.UserData); + } + + UpdateControls(a.GetUpgradableSpecBlock()); + } + + public static void InitControls() where Z : IMyCubeBlock + { + try + { + var upgradesListBox = MyAPIGateway.TerminalControls.CreateControl("AvailableUpgrades"); + upgradesListBox.Visible = (x) => x.GetUpgradableSpecBlock()?.CanPickUpgrades() ?? false; + upgradesListBox.VisibleRowsCount = 12; + upgradesListBox.Multiselect = false; + upgradesListBox.ItemSelected = UpgradesListBoxItemSelected; + upgradesListBox.ListContent = UpgradeListContentGui; + MyAPIGateway.TerminalControls.AddControl(upgradesListBox); + + MyAPIGateway.TerminalControls.CreateButton("AddUpgrade", T.Translation(T.Button_AddUpgrade), T.Translation(T.Button_AddUpgrade_Tooltip), + AddUpgradeGui, + Sugar2.GetUpgradableSpecBlock, + enabled: AddUpgrade_Enabled, + visible: UpgradeGui_IsVisible); + + MyAPIGateway.TerminalControls.CreateButton("RemoveUpgrade", T.Translation(T.Button_RemoveUpgrade), T.Translation(T.Button_RemoveUpgrade_Tooltip), + RemoveUpgradeGui, + Sugar2.GetUpgradableSpecBlock, + visible: UpgradeGui_IsVisible); + + MyAPIGateway.TerminalControls.CreateButton("ApplyUpgrades", T.Translation(T.Button_ApplyUpgrade), T.Translation(T.Button_ApplyUpgrade_Tooltip), + ApplyUpgradesGui, + Sugar2.GetUpgradableSpecBlock, + enabled: ApplyUpgrades_Enabled, + visible: UpgradeGui_IsVisible).DoubleClick(); + + //MyAPIGateway.TerminalControls.CreateButton("ApplyRandomUpgrades", + // T.Translation(T.Button_ApplyRandomUpgrade), + // T.Translation(T.Button_ApplyRandomUpgrade_Tooltip), + // ApplyRandomUpgradesGui, + // Sugar2.GetUpgradableSpecBlock, + // enabled: (x) => AddedTest.Count == 0 && x.CanPickUpgrades(), + // visible: (x) => x.info.ExtraRandomUpgrades > 0 && x.CanPickUpgrades()).DoubleClick(); + + } + catch (Exception e) + { + Log.ChatError(e); + } + } + + + + + + private static void AddUpgradeGui(SpecBlock x) + { + //TODO + if (x.SelectedUpgrades.Count != 1) { return; } + + if (new UpgradeHelper(x, x.AddedUpgrades, x.SelectedUpgrades[0]).CanBeApplied()) + { + x.AddedUpgrades.Add(x.SelectedUpgrades[0]); + } + + x.SelectedUpgrades.Clear(); + x.block.RefreshDI(); + UpdateControls(x); + } + + private static void RemoveUpgradeGui(SpecBlock x) + { + foreach (var a in x.SelectedUpgrades) + { + for (int i = x.AddedUpgrades.Count - 1; i >= 0; i--) + { + if (x.AddedUpgrades[i] == a) + { + x.AddedUpgrades.RemoveAt(i); + break; + } + } + } + + var newPossible = new UpgradeHelper(x, x.AddedUpgrades).GetMaxPossibleUpgradeList(); + x.AddedUpgrades.Clear(); + x.AddedUpgrades.AddRange(newPossible); + + x.SelectedUpgrades.Clear(); + x.block.RefreshDI(); + UpdateControls(x); + } + + private static void ApplyUpgradesGui(SpecBlock x) + { + var settings = new UpgradableSpecBlockSettings(); + settings.Upgrades.AddRange(x.AddedUpgrades); + x.AddedUpgrades.Clear(); + x.SelectedUpgrades.Clear(); + Sync.SendMessageToServer(x.block.EntityId, settings, type: APPLY_SELECTED); + FrameExecutor.addDelayedLogic(30, (xx) => UpdateControls(x)); //1/2 sec + if (!MyAPIGateway.Session.IsServer) + { + x.Settings = settings; //Remove gui lag + } + } + + private static void ApplyRandomUpgradesGui(SpecBlock x) + { + x.AddedUpgrades.Clear(); + x.SelectedUpgrades.Clear(); + + var settings = new UpgradableSpecBlockSettings(); + settings.Upgrades = new List(); + Sync.SendMessageToServer(x.block.EntityId, x.Settings, type: APPLY_RANDOM); //1/2 sec + FrameExecutor.addDelayedLogic(30, (xx) => UpdateControls(x)); + if (!MyAPIGateway.Session.IsServer) { + x.Settings = settings; //Remove gui lag + } + } + + public static Dictionary GetTotalUpgradeCost(Dictionary upgrades) + { + var sum = new Dictionary(); + foreach (var u in upgrades) + { + var upgrade = OriginalSpecCoreSession.Instance.Upgrades.GetOr(u.Key, null); + sum.Sum(upgrade.Levels[u.Value-1].TotalUpgradeCost); + } + + return sum; + } + + private static void UpgradeListContentGui(IMyTerminalBlock a, List all, List selected) + { + try + { + UpgradeListContentGui2(a, all, selected); + } + catch (Exception e) + { + Log.ChatError(e); + } + } + private static void UpgradeListContentGui2(IMyTerminalBlock a, List all, List selected) + { + var specBlock = a.GetUpgradableSpecBlock(); + all.Clear(); + selected.Clear(); + + Limits originalStatic, originalDynamic; + + var upgrades = specBlock.AddedUpgrades.SumDuplicates(); + specBlock.GetUpgradedValues(upgrades, out originalStatic, out originalDynamic, false); + var tooltipSb = new StringBuilder(); + + + + + var maxPoints = specBlock.GetLimits(); + OriginalSpecCoreSession.RemoveNonUpgradePoints(maxPoints); + + + var possibleUpgrades = OriginalSpecCoreSession.Instance.Upgrades; + foreach (var u in specBlock.info.PossibleUpgrades) + { + if (!possibleUpgrades.ContainsKey(u)) + { + Log.ChatError($"Wasn't able to find upgrade with id {u}"); + continue; + } + + var up = possibleUpgrades[u]; + if (!new UpgradeHelper(specBlock, specBlock.AddedUpgrades, u).CanBeApplied()) + { + if (specBlock.AddedUpgrades.Contains(u)) + { + var item = GetRowItem(specBlock, up, specBlock.AddedUpgrades, upgrades, maxPoints, u, ""); + all.Add(item); + if (specBlock.SelectedUpgrades.Contains(u)) + { + selected.Add(item); + } + } + } + else + { + var tooltip = GetTooltip(specBlock, u, upgrades, originalStatic, originalDynamic, tooltipSb); + var item = GetRowItem(specBlock, up, specBlock.AddedUpgrades, upgrades, maxPoints, u, tooltip); + all.Add(item); + + if (specBlock.SelectedUpgrades.Contains(u)) + { + selected.Add(item); + } + } + } + } + + private static MyTerminalControlListBoxItem GetRowItem(SpecBlock specBlock, Upgrade up, List upgradeOrder, Dictionary upgrades, Limits maxPoints, int u, string tooltip) + { + var lvl = upgrades.GetOr(up.NId, 0) - 1; + + var upgradeName = T.Translation(up.DisplayName); + + String upText = ""; + int maxLevel = up.Levels.Length; + int nextLevel = lvl + 1; + if (nextLevel >= up.Levels.Length) + { + upText = T.Translation(T.UpgradesList_UpgradeTooltip_MaxUpgrades); + } + else + { + var upgradesAfter = new Dictionary(upgrades); + upgradesAfter.Sum(up.NId, 1); + + var sumBeforeUpgrade = GetTotalUpgradeCost(upgrades); + var sumAfterUpgrade = GetTotalUpgradeCost(upgradesAfter); + + var totalCost = new Dictionary(up.Levels[nextLevel].TotalUpgradeCost); + + var upgradeCost = new Dictionary(sumAfterUpgrade); + upgradeCost.Minus(sumBeforeUpgrade); + upgradeCost.RemoveWhere((x,y)=>y == 0); + + var wouldHavePointsLeft = new Dictionary(maxPoints); + wouldHavePointsLeft.Minus(sumAfterUpgrade); + wouldHavePointsLeft.RemoveWhere((x,y)=>y == 0); + + var wouldMissPoints = new Dictionary(wouldHavePointsLeft); + wouldMissPoints.RemoveWhere((x,y)=>y >= 0); + + var sb = new StringBuilder(); + if (wouldMissPoints.Count > 0) + { + var missing = PrintUpgradePoints(wouldMissPoints, T.UpgradesList_UpgradeTooltip_NotEnoughPoints_Row, sb); + upText = T.Translation(T.UpgradesList_UpgradeTooltip_NotEnoughPoints, missing); + } + else + { + var wouldHavePointsText = PrintUpgradePoints(wouldHavePointsLeft, T.UpgradesList_UpgradeTooltip_WouldHavePointsLeft, T.UpgradesList_UpgradeTooltip_WouldHavePointsLeftZero, T.UpgradesList_UpgradeTooltip_Row, sb.Clear()); + var upgradeCostText = PrintUpgradePoints(upgradeCost, T.UpgradesList_UpgradeTooltip_UpgradeCost, T.UpgradesList_UpgradeTooltip_UpgradeCostZero,T.UpgradesList_UpgradeTooltip_Row, sb.Clear()); + var totalCostText = PrintUpgradePoints(totalCost, T.UpgradesList_UpgradeTooltip_TotalCost, T.UpgradesList_UpgradeTooltip_TotalCostZero,T.UpgradesList_UpgradeTooltip_Row, sb.Clear()); + + var was = new UpgradeHelper(specBlock, upgradeOrder); + var wouldBe = new UpgradeHelper(specBlock, upgradeOrder, up.NId); + var locksAndUnlocks = was.GetLocksAndUnlocks(wouldBe); + + var sbLocks = new StringBuilder(); + var sbUnlocks = new StringBuilder(); + foreach (var locksAndUnlock in locksAndUnlocks) + { + if (locksAndUnlock.Value) + { + sbLocks.Append(locksAndUnlock.Key); + } + else + { + sbUnlocks.Append(locksAndUnlock.Key); + } + } + + upText = T.Translation(T.UpgradesList_UpgradeTooltip, + nextLevel, nextLevel+1, maxLevel, + wouldHavePointsText, upgradeCostText,totalCostText, upgradeName, + sbLocks.ToString(), sbUnlocks.ToString()); + } + } + + tooltip = tooltip ?? ""; + + + var rowText = T.Translation(T.UpgradesList_RowText, upgradeName, nextLevel, nextLevel+1, maxLevel); + + var item = new MyTerminalControlListBoxItem(MyStringId.GetOrCompute(rowText), MyStringId.GetOrCompute(upText + tooltip), u); + return item; + } + + + private static bool AddUpgrade_Enabled(SpecBlock specBlock) + { + if (specBlock.SelectedUpgrades.Count == 0) return false; + var copy = new List(specBlock.AddedUpgrades); + copy.AddRange(specBlock.SelectedUpgrades); + return new UpgradeHelper(specBlock, copy).CanBeApplied(); + } + + private static bool ApplyUpgrades_Enabled(SpecBlock specBlock) { return specBlock.AddedUpgrades.Count > 0; } + + private static bool UpgradeGui_IsVisible(SpecBlock specBlock) + { + return specBlock.CanPickUpgrades(); + } + + + + + #region Helpers + + private static string PrintUpgradePoints(Dictionary points, string translationHasPoints, string translationNoPoints, string rowTranslation, StringBuilder sb) + { + var s = PrintUpgradePoints(points, rowTranslation, sb); + return T.Translation(points.Count == 0 ? translationNoPoints : translationHasPoints, s); + } + + private static string PrintUpgradePoints(Dictionary points, string rowTranslation, StringBuilder sb) + { + sb = sb ?? new StringBuilder(); + + foreach (var point in points) + { + var up = OriginalSpecCoreSession.Instance.Points[point.Key]; + var name = T.Translation(up.Name); + sb.AppendT(rowTranslation, name, point.Value); + } + + return sb.ToString(); + } + + #endregion + + + + private static List GenerateRandomUpgrades(List possibleUpgrades, int upgradesLeft) + { + //TODO + + var upgrades = OriginalSpecCoreSession.Instance.Upgrades; + for (var index = 0; index < possibleUpgrades.Count; index++) + { + if (!upgrades.ContainsKey(possibleUpgrades[index])) + { + possibleUpgrades.RemoveAt(index); + index--; + } + } + + Dictionary outUpgrades = new Dictionary(); + var r = new Random(); + while (possibleUpgrades.Count > 0 && upgradesLeft > 0) + { + var u = r.Next(possibleUpgrades); + var lvl = outUpgrades.GetOr(u, 0); + var up = upgrades[u]; + + var nextUpgradeCost = 0; //up.GetNextUpgradeCost(lvl); + if (nextUpgradeCost > upgradesLeft) + { + //Log.ChatError($"Skipping {up.Name} {lvl}lvl {nextUpgradeCost} : {upgradesLeft}"); + possibleUpgrades.Remove(u); + } + else + { + //Log.ChatError($"Adding {up.Name} {lvl}lvl {nextUpgradeCost} : {upgradesLeft}"); + outUpgrades.Sum(u, 1); + //if (lvl >= up.MaxUpgrades) + //{ + // possibleUpgrades.Remove(u); + // //Log.ChatError($"Max ugrade: {up.Name}"); + //} + upgradesLeft -= nextUpgradeCost; + } + } + + var upgradesList = new List(); + foreach (var upgrade in outUpgrades) + { + for (int i = 0; i < upgrade.Value; i++) + { + upgradesList.Add(upgrade.Key); + } + } + + return upgradesList; + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/UpgradeHelper.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/UpgradeHelper.cs new file mode 100644 index 00000000..85bd290b --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlock/UpgradeHelper.cs @@ -0,0 +1,203 @@ +using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.CSharp; +using MIG.Shared.SE; + +namespace MIG.SpecCores +{ + public class UpgradeHelper + { + public Dictionary> hardLocks = new Dictionary>(); + public Dictionary> softLocks = new Dictionary>(); + public UpgradeLevel lastUpgradeLevel; + public List desiredUpgrades; + public int index; + + public UpgradeHelper(SpecBlock specBlock, List desiredUpgradesOriginal, int extraUpgrade) + { + var copy = new List(desiredUpgradesOriginal); + copy.Add(extraUpgrade); + desiredUpgrades = copy; + index = GetIndexOfLastUpgradeThatCanBeApplied(specBlock, desiredUpgrades); + } + + public UpgradeHelper(SpecBlock specBlock, List desiredUpgradesOriginal) + { + var copy = new List(desiredUpgradesOriginal); + desiredUpgrades = copy; + index = GetIndexOfLastUpgradeThatCanBeApplied(specBlock, copy); + } + + + public bool CanBeApplied() + { + return index == desiredUpgrades.Count - 1; + } + + public List GetMaxPossibleUpgradeList() + { + var copy = new List(); + for (int i = 0; i <= index; i++) + { + copy.Add(desiredUpgrades[i]); + } + return copy; + } + + public Dictionary GetLocksAndUnlocks(UpgradeHelper helper) + { + var diff = new Dictionary(); + + var wasHardLocks = new Dictionary>(hardLocks); + var wasSoftLocks = new Dictionary>(softLocks); + var wouldBeHardLocks = new Dictionary>(helper.hardLocks); + var wouldBeSoftLocks = new Dictionary>(helper.hardLocks); + + //wasHardLocks.RemoveDuplicateKeys(wouldBeHardLocks); + //wasSoftLocks.RemoveDuplicateKeys(wouldBeSoftLocks); + //wouldBeHardLocks.RemoveDuplicateKeys(wasHardLocks); + //wouldBeSoftLocks.RemoveDuplicateKeys(wasSoftLocks); + + foreach (var hardLock in wasHardLocks) + { + diff[hardLock.Key] = false; + } + foreach (var softLock in wasSoftLocks) + { + diff[softLock.Key] = false; + } + + foreach (var hardLock in wouldBeHardLocks) + { + diff[hardLock.Key] = true; + } + foreach (var softLock in wouldBeSoftLocks) + { + diff[softLock.Key] = true; + } + + return diff; + } + + private int GetIndexOfLastUpgradeThatCanBeApplied(SpecBlock specBlock, List desiredUpgrades) + { + var line = 0; + + try + { + var limits = specBlock.GetLimits(false); + OriginalSpecCoreSession.RemoveNonUpgradePoints(limits); + + Dictionary usedPoints = new Dictionary(); + Dictionary currentUpgrades = new Dictionary(); + + HashSet currentLockGroups = new HashSet(); + + + line = 1; + + int index = -1; + foreach (var u in desiredUpgrades) + { + currentUpgrades.Sum(u, 1); + var upgrades = OriginalSpecCoreSession.Instance.Upgrades; + if (!upgrades.ContainsKey(u)) + { + return index; + } + + line = 2; + var upgrade = upgrades[u]; + var currentUpgradeLevel = currentUpgrades[u]-1; + + if (currentUpgradeLevel >= upgrade.Levels.Length) + { + //Log.ChatError($"Going over max level {currentUpgradeLevel+1} / {upgrade.Levels.Length}"); + return index; + } + + line = 3; + + var level = upgrade.Levels[currentUpgradeLevel]; + foreach (var group in level.Locks.LockGroups) + { + if ((hardLocks.GetOr(group, null)?.Count ?? 0) > 0 || (softLocks.GetOr(group, null)?.Count ?? 0) > 0 ) + { + //Log.ChatError($"Blocked upgrade {u}"); + return index; + } + } + + + line = 4; + + if (level.Locks.AddHardLocks.Length > 0) + { + foreach (var lockName in level.Locks.AddHardLocks) + { + if (currentLockGroups.Contains(lockName)) return index; + } + } + + line = 5; + + if (currentUpgradeLevel != 0) + { + var prev = upgrade.Levels[currentUpgradeLevel-1]; + foreach (var cost in prev.Costs) + { + usedPoints.Sum(cost.PointId, -cost.Value); + } + } + + line = 6; + + foreach (var cost in level.Costs) + { + usedPoints.Sum(cost.PointId, cost.Value); + } + + line = 7; + + if (usedPoints.IsOneKeyMoreThan(limits)) + { + //Log.ChatError($"One key is over max upgrades {usedPoints.Print(" ", SharedLogic.NoZeroLimits)} / {limits.Print(" ", SharedLogic.NoZeroLimits)}"); + return index; + } + + line = 8; + + //Remove old and add new upgrades + if (currentUpgradeLevel != 0) + { + foreach (var blockedUpgrade in hardLocks) blockedUpgrade.Value.Remove(upgrade); + foreach (var blockedUpgrade in softLocks) blockedUpgrade.Value.Remove(upgrade); + } + + line = 9; + + foreach (var blockedId in level.Locks.AddHardLocks) hardLocks.GetOrNew(blockedId).Add(upgrade); + foreach (var blockedId in level.Locks.AddSoftLocks) softLocks.GetOrNew(blockedId).Add(upgrade); + + foreach (var blockedId in level.Locks.RemoveHardLocks) { hardLocks.Remove(blockedId); currentLockGroups.Remove(blockedId); } + foreach (var blockedId in level.Locks.RemoveHardLocks) { softLocks.Remove(blockedId); currentLockGroups.Remove(blockedId); } + + foreach (var g in level.Locks.LockGroups) currentLockGroups.Add(g); + + lastUpgradeLevel = level; + + line = 10; + + index++; + } + return index; + } + catch (Exception e) + { + Log.ChatError($"GetIndexOfLastUpgradeThatCanBeApplied line={line} {e}"); + return 0; + } + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlockHooks.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlockHooks.cs new file mode 100644 index 00000000..292d8354 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/SpecBlockHooks.cs @@ -0,0 +1,245 @@ +/*using System; +using System.Collections.Generic; +using Digi; +using MIG.Shared.SE; +using Sandbox.ModAPI; +using VRage.Game; +using VRage.Game.ModAPI; +using F = System.Func; +using FF = System.Func; +using A = System.Action; +using C = System.Func; +using U = System.Collections.Generic.List; +using L = System.Collections.Generic.IDictionary; + +namespace Scripts.Specials.ShipClass +{ + public class SpecBlockHooks + { + public interface ILimitedBlock + { + bool IsDrainingPoints(); + void Disable(); + void CanWork(); + bool CanBeDisabled(); + bool CheckConditions(IMyTerminalBlock specBlock); + } + + public enum GetSpecCoreLimitsEnum + { + StaticLimits = 1, + DynamicLimits = 2, + FoundLimits = 3, + TotalLimits = 4, + CustomStatic = 5, + CustomDynamic = 6, + CurrentStaticOrDynamic = 7 + } + + + private static Action RegisterCustomLimitConsumerImpl; + private static Func getMainSpecCore; + private static Func getMainSpecCoreBlock; + private static Action getSpecCoreLimits; + private static Action getSpecCoreUpgrades; + private static Action setSpecCoreCustomValues; + + private static Func getSpecCoreBlock; + private static Func getBlockSpecCore; + private static Func getLimitedBlock; + private static Func getLimitedBlockBlock; + + private static Func>> getGridBlocksByType; + private static Func>> getGridBlocksById; + + private static Action , float>> registerSpecCorePointCustomFx; + private static Action, Dictionary>> addSpecCoreLimitsInterceptor; + + public static event Action OnReady; + + public static event Action OnSpecBlockCreated; + public static event Action OnSpecBlockDestroyed; + public static event Action OnLimitedBlockCreated; + public static event Action OnLimitedBlockDestroyed; + + public static event Action> OnSpecBlockChanged; + + public static bool IsReady() + { + return + RegisterCustomLimitConsumerImpl != null && + getMainSpecCore != null && + getMainSpecCoreBlock != null && + getSpecCoreLimits != null && + getSpecCoreUpgrades != null && + setSpecCoreCustomValues != null && + + getSpecCoreBlock != null && + getBlockSpecCore != null && + getLimitedBlock != null && + getLimitedBlockBlock != null && + + getGridBlocksByType != null && + getGridBlocksById != null && + + + addSpecCoreLimitsInterceptor != null && + registerSpecCorePointCustomFx != null; + } + + private static void TriggerIsReady() + { + if (IsReady()) + { + OnReady?.Invoke(); + } + } + + /// + /// Must be inited in LoadData of MySessionComponentBase + /// + public static void Init() + { + Log.ChatError("SpecBlockHooks:Init"); + ModConnection.Init(); + ModConnection.Subscribe>("MIG.SpecCores.RegisterCustomLimitConsumer", (x) => { RegisterCustomLimitConsumerImpl = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetMainSpecCore", (x) => { getMainSpecCore = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetMainSpecCoreBlock", (x) => { getMainSpecCoreBlock = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetSpecCoreLimits", (x) => { getSpecCoreLimits = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetSpecCoreUpgrades", (x) => { getSpecCoreUpgrades = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.SetSpecCoreCustomValues", (x) => { setSpecCoreCustomValues = x; TriggerIsReady(); }); + + ModConnection.Subscribe>>>("MIG.SpecCores.GetGridBlocksByType", (x) => { getGridBlocksByType = x; TriggerIsReady(); }); + ModConnection.Subscribe>>>("MIG.SpecCores.GetGridBlocksById", (x) => { getGridBlocksById = x; TriggerIsReady(); }); + + ModConnection.Subscribe>("MIG.SpecCores.GetSpecCoreBlock", (x) => { getSpecCoreBlock = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetBlockSpecCore", (x) => { getBlockSpecCore = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetLimitedBlock", (x) => { getLimitedBlock = x; TriggerIsReady(); }); + ModConnection.Subscribe>("MIG.SpecCores.GetLimitedBlockBlock", (x) => { getLimitedBlockBlock = x; TriggerIsReady(); }); + + ModConnection.Subscribe, float>>>("MIG.SpecCores.RegisterSpecCorePointCustomFx", (x) => { registerSpecCorePointCustomFx = x; TriggerIsReady(); }); + ModConnection.Subscribe, Dictionary>>>("MIG.SpecCores.AddSpecCoreLimitsInterceptor", (x)=>{ addSpecCoreLimitsInterceptor = x; TriggerIsReady(); }); + + + Action onSpecBlockCreated = (x) => OnSpecBlockCreated?.Invoke(x); + Action onSpecBlockDestroyed = (x) => OnSpecBlockDestroyed?.Invoke(x); + Action onLimitedBlockCreated = (x) => OnLimitedBlockCreated?.Invoke(x); + Action onLimitedBlockDestroyed = (x) => OnLimitedBlockDestroyed?.Invoke(x); + + Action> onSpecBlockChanged = (x,y) => OnSpecBlockChanged?.Invoke(x,y); + + ModConnection.SetValue("MIG.SpecCores.OnSpecBlockCreated", onSpecBlockCreated); + ModConnection.SetValue("MIG.SpecCores.OnLimitedBlockCreated", onSpecBlockDestroyed); + ModConnection.SetValue("MIG.SpecCores.OnSpecBlockDestroyed", onLimitedBlockCreated); + ModConnection.SetValue("MIG.SpecCores.OnLimitedBlockDestroyed", onLimitedBlockDestroyed); + + ModConnection.SetValue("MIG.SpecCores.OnSpecBlockChanged", onSpecBlockChanged); + } + + public static void Close() + { + ModConnection.Close(); + } + + public static void SetCanSpecCoreWorkFx(Func, string> fx) + { + ModConnection.SetValue("MIG.SpecCores.CanSpecCoreWork", fx); + } + + public static void RegisterSpecCorePointCustomFx(int pointId, Func, float> fx) + { + registerSpecCorePointCustomFx.Invoke(pointId, fx); + } + + public static void RegisterCustomLimitConsumer(string Id, C OnNewConsumerRegistered, A CanWork, FF CheckConditions, F CanBeDisabled, F IsDrainingPoints, A Disable) + { + RegisterCustomLimitConsumerImpl(Id, OnNewConsumerRegistered, CanWork, CheckConditions, CanBeDisabled, IsDrainingPoints, Disable); + } + + public static object GetMainSpecCore(IMyCubeGrid grid) + { + return getMainSpecCore.Invoke(grid); + } + + public static IMyTerminalBlock GetMainSpecCoreBlock(IMyCubeGrid grid) + { + return getMainSpecCoreBlock.Invoke(grid); + } + + public static void GetSpecCoreLimits(object specCore, IDictionary buffer, GetSpecCoreLimitsEnum limits) + { + getSpecCoreLimits.Invoke(specCore, buffer, (int) limits); + } + + public static void GetSpecCoreUpgrades(object specCore, List buffer) + { + getSpecCoreUpgrades.Invoke(specCore, buffer); + } + + public static void SetSpecCoreCustomValues(object specCore, IDictionary staticValues, IDictionary dynamicValues) + { + setSpecCoreCustomValues.Invoke(specCore, staticValues, dynamicValues); + } + + public static void AddSpecCoreLimitsInterceptor(Action, Dictionary> fx) + { + addSpecCoreLimitsInterceptor.Invoke(fx); + } + + public static object GetSpecCoreBlock(IMyTerminalBlock block) + { + return getSpecCoreBlock.Invoke(block); + } + + public static IMyTerminalBlock GetBlockSpecCore(object block) + { + return getBlockSpecCore.Invoke(block); + } + + public static object GetLimitedBlock(IMyTerminalBlock block) + { + return getLimitedBlock.Invoke(block); + } + + public static IMyTerminalBlock GetLimitedBlockBlock(object block) + { + return getLimitedBlockBlock.Invoke(block); + } + + public static void RegisterCustomLimitConsumer(string Id, Func creator) + { + SpecBlockHooks.RegisterCustomLimitConsumer(Id, + creator, + (logic)=> + { + ((ILimitedBlock) logic).CanWork(); + }, + (block, logic) => + { + return ((ILimitedBlock) logic).CheckConditions(block); + }, + (logic)=> + { + return ((ILimitedBlock) logic).CanBeDisabled(); + }, + (logic)=> + { + return ((ILimitedBlock) logic).IsDrainingPoints(); + }, + (logic)=> + { + ((ILimitedBlock) logic).Disable(); + }); + } + + public static Dictionary> GetGridBlocksByType(IMyCubeGrid grid) + { + return getGridBlocksByType?.Invoke(grid) ?? null; + } + + public static Dictionary> GetGridBlocksById(IMyCubeGrid grid) + { + return getGridBlocksById?.Invoke(grid) ?? null; + } + } +}*/ \ No newline at end of file diff --git a/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/TorchExtensions.cs b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/TorchExtensions.cs new file mode 100644 index 00000000..01b0dcd8 --- /dev/null +++ b/TSTSSESCores/Data/Scripts/Scripts/Specials/ShipClass/TorchExtensions.cs @@ -0,0 +1,37 @@ +using System; +using Digi; +using MIG.Shared.SE; +using VRage.Game; +using VRage.Game.Entity; +using VRage.Game.ModAPI; + +namespace MIG.SpecCores +{ + public static class TorchExtensions + { + public static Action IncreaseMountLevel = + ( + block, + welderMountAmount, + welderOwnerIdentId, + outputInventory, + maxAllowedBoneMovement, + isHelping, + sharing, + handWelded) => + { + block.IncreaseMountLevel(welderMountAmount, welderOwnerIdentId, (IMyInventory) outputInventory, + maxAllowedBoneMovement, isHelping, sharing); + + if (OriginalSpecCoreSession.IsDebug) + { + Log.ChatError("Weld may work incorrectly in SP"); + } + }; + + public static void Init() + { + ModConnection.Subscribe("MIG.APIExtender.IncreaseMountLevel", IncreaseMountLevel, (func) => IncreaseMountLevel = func); + } + } +} \ No newline at end of file diff --git a/TSTSSESCores/metadata.mod b/TSTSSESCores/metadata.mod new file mode 100644 index 00000000..0a020fdf --- /dev/null +++ b/TSTSSESCores/metadata.mod @@ -0,0 +1,4 @@ + + + 1.0 + \ No newline at end of file diff --git a/TSTSSESCoresAddon-MultiCoreExperiment.zip b/TSTSSESCoresAddon-MultiCoreExperiment.zip new file mode 100644 index 00000000..0183deb7 Binary files /dev/null and b/TSTSSESCoresAddon-MultiCoreExperiment.zip differ diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Destroyer.cs b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Destroyer.cs deleted file mode 100644 index 546089b5..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Destroyer.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Sandbox.Common.ObjectBuilders; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using VRage.ModAPI; -using VRage; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.ObjectBuilders; -using VRageMath; -using System.Collections.Generic; -using System.Linq; -using VRage.Game.ModAPI.Ingame; -using IMyEntity = VRage.Game.ModAPI.Ingame.IMyEntity; -using IMyCubeBlock = VRage.Game.ModAPI.IMyCubeBlock; -using IMySlimBlock = VRage.Game.ModAPI.IMySlimBlock; -using System.Text; - -namespace TSTSSESCoresAddon.Data.Scripts.ScriptsAddon.CoreLogics -{ - [MyEntityComponentDescriptor(typeof(MyObjectBuilder_Beacon), false, "TSTSSES_DestroyerCore")] - public class CoreLogic_Destroyer : MyGameLogicComponent - { - private IMyCubeBlock block; - private const string DestroyerReactorSubtype = "DestroyerCore_Reactor"; - private const string DestroyerCargoSubtype = "DestroyerCore_Cargo"; - private const int MaxDistance = 2; - private const int MaxDestroyerReactors = 1; - private const int MaxDestroyerCargos = 1; - private const double NotificationRadius = 50.0; // Radius for player notification - - public override void Init(MyObjectBuilder_EntityBase objectBuilder) - { - base.Init(objectBuilder); - block = (IMyCubeBlock)Entity; - - // Periodic check to ensure the assembly is intact - NeedsUpdate |= MyEntityUpdateEnum.EACH_100TH_FRAME; - } - - public override void UpdateAfterSimulation100() - { - string errorMessages = IsAssemblyIntact(); - if (!string.IsNullOrEmpty(errorMessages)) - { - IMyTerminalBlock terminalBlock = block as IMyTerminalBlock; // Cast to IMyTerminalBlock - string notificationText = $"[Grid: {block.CubeGrid.DisplayName}] '{terminalBlock.CustomName}' status:\n{errorMessages}"; - NotifyPlayersInRange(notificationText, block.GetPosition(), NotificationRadius, MyFontEnum.Red); - } - } - - - public void NotifyPlayersInRange(string text, Vector3D position, double radius, string font) - { - var bound = new BoundingSphereD(position, radius); - List nearEntities = MyAPIGateway.Entities.GetEntitiesInSphere(ref bound); - - foreach (var entity in nearEntities) - { - var character = entity as IMyCharacter; - if (character != null && character.IsPlayer && bound.Contains(character.GetPosition()) != ContainmentType.Disjoint) - { - var notification = MyAPIGateway.Utilities.CreateNotification(text, 1600, font); - notification.Show(); - } - } - } - - - - private string IsAssemblyIntact() - { - var grid = block.CubeGrid; - var corePosition = block.Position; - - // List all blocks of the specific subtypes on the grid - var allReactorBlocks = new List(); - var allCargoBlocks = new List(); - - grid.GetBlocks(allReactorBlocks, b => b.FatBlock != null && b.FatBlock.BlockDefinition.SubtypeId == DestroyerReactorSubtype); - grid.GetBlocks(allCargoBlocks, b => b.FatBlock != null && b.FatBlock.BlockDefinition.SubtypeId == DestroyerCargoSubtype); - - // Filter the blocks to only those that are adjacent to the core - var adjacentReactorBlocks = allReactorBlocks.Where(b => Vector3I.DistanceManhattan(b.Position, corePosition) <= MaxDistance).ToList(); - var adjacentCargoBlocks = allCargoBlocks.Where(b => Vector3I.DistanceManhattan(b.Position, corePosition) <= MaxDistance).ToList(); - - StringBuilder errorMessage = new StringBuilder(); - - // Check if the number of adjacent blocks is within the allowed range - int reactorCount = adjacentReactorBlocks.Count; - int cargoCount = adjacentCargoBlocks.Count; - - if (reactorCount > MaxDestroyerReactors) - { - errorMessage.AppendLine($"Exceeds maximum {DestroyerReactorSubtype} count (Max: {MaxDestroyerReactors})."); - } - else if (reactorCount < MaxDestroyerReactors) - { - errorMessage.AppendLine($"{DestroyerReactorSubtype} required."); - } - - if (cargoCount > MaxDestroyerCargos) - { - errorMessage.AppendLine($"Exceeds maximum {DestroyerCargoSubtype} count (Max: {MaxDestroyerCargos})."); - } - else if (cargoCount < MaxDestroyerCargos) - { - errorMessage.AppendLine($"{DestroyerCargoSubtype} required."); - } - - // Backup check for non-adjacent reactors or cargos - if (allReactorBlocks.Count > reactorCount) - { - errorMessage.AppendLine($"Detected non-adjacent {DestroyerReactorSubtype} blocks."); - } - - if (allCargoBlocks.Count > cargoCount) - { - errorMessage.AppendLine($"Detected non-adjacent {DestroyerCargoSubtype} blocks."); - } - - return errorMessage.ToString().Trim(); - } - - - - public override void Close() - { - base.Close(); - // Additional cleanup if needed - } - } -} diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Frigate.cs b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Frigate.cs deleted file mode 100644 index 025c090c..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/CoreLogics/CoreLogic_Frigate.cs +++ /dev/null @@ -1,133 +0,0 @@ -using Sandbox.Common.ObjectBuilders; -using Sandbox.Game.Entities; -using Sandbox.ModAPI; -using VRage.ModAPI; -using VRage; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.ObjectBuilders; -using VRageMath; -using System.Collections.Generic; -using System.Linq; -using VRage.Game.ModAPI.Ingame; -using IMyEntity = VRage.Game.ModAPI.Ingame.IMyEntity; -using IMyCubeBlock = VRage.Game.ModAPI.IMyCubeBlock; -using IMySlimBlock = VRage.Game.ModAPI.IMySlimBlock; -using System.Text; - -namespace TSTSSESCoresAddon.Data.Scripts.ScriptsAddon.CoreLogics -{ - [MyEntityComponentDescriptor(typeof(MyObjectBuilder_Beacon), false, "TSTSSES_FrigateCore")] - public class CoreLogic_Frigate : MyGameLogicComponent - { - private IMyCubeBlock block; - private const string FrigateReactorSubtype = "FrigateCore_Reactor"; - private const string FrigateCargoSubtype = "FrigateCore_Cargo"; - private const int MaxDistance = 1; - private const int MaxFrigateReactors = 1; - private const int MaxFrigateCargos = 1; - private const double NotificationRadius = 50.0; // Radius for player notification - - public override void Init(MyObjectBuilder_EntityBase objectBuilder) - { - base.Init(objectBuilder); - block = (IMyCubeBlock)Entity; - - // Periodic check to ensure the assembly is intact - NeedsUpdate |= MyEntityUpdateEnum.EACH_100TH_FRAME; - } - - public override void UpdateAfterSimulation100() - { - string errorMessages = IsAssemblyIntact(); - if (!string.IsNullOrEmpty(errorMessages)) - { - IMyTerminalBlock terminalBlock = block as IMyTerminalBlock; // Cast to IMyTerminalBlock - string notificationText = $"[Grid: {block.CubeGrid.DisplayName}] '{terminalBlock.CustomName}' status:\n{errorMessages}"; - NotifyPlayersInRange(notificationText, block.GetPosition(), NotificationRadius, MyFontEnum.Red); - } - } - - - public void NotifyPlayersInRange(string text, Vector3D position, double radius, string font) - { - var bound = new BoundingSphereD(position, radius); - List nearEntities = MyAPIGateway.Entities.GetEntitiesInSphere(ref bound); - - foreach (var entity in nearEntities) - { - var character = entity as IMyCharacter; - if (character != null && character.IsPlayer && bound.Contains(character.GetPosition()) != ContainmentType.Disjoint) - { - var notification = MyAPIGateway.Utilities.CreateNotification(text, 1600, font); - notification.Show(); - } - } - } - - - - private string IsAssemblyIntact() - { - var grid = block.CubeGrid; - var corePosition = block.Position; - - // List all blocks of the specific subtypes on the grid - var allReactorBlocks = new List(); - var allCargoBlocks = new List(); - - grid.GetBlocks(allReactorBlocks, b => b.FatBlock != null && b.FatBlock.BlockDefinition.SubtypeId == FrigateReactorSubtype); - grid.GetBlocks(allCargoBlocks, b => b.FatBlock != null && b.FatBlock.BlockDefinition.SubtypeId == FrigateCargoSubtype); - - // Filter the blocks to only those that are adjacent to the core - var adjacentReactorBlocks = allReactorBlocks.Where(b => Vector3I.DistanceManhattan(b.Position, corePosition) <= MaxDistance).ToList(); - var adjacentCargoBlocks = allCargoBlocks.Where(b => Vector3I.DistanceManhattan(b.Position, corePosition) <= MaxDistance).ToList(); - - StringBuilder errorMessage = new StringBuilder(); - - // Check if the number of adjacent blocks is within the allowed range - int reactorCount = adjacentReactorBlocks.Count; - int cargoCount = adjacentCargoBlocks.Count; - - if (reactorCount > MaxFrigateReactors) - { - errorMessage.AppendLine($"Exceeds maximum {FrigateReactorSubtype} count (Max: {MaxFrigateReactors})."); - } - else if (reactorCount < MaxFrigateReactors) - { - errorMessage.AppendLine($"{FrigateReactorSubtype} required."); - } - - if (cargoCount > MaxFrigateCargos) - { - errorMessage.AppendLine($"Exceeds maximum {FrigateCargoSubtype} count (Max: {MaxFrigateCargos})."); - } - else if (cargoCount < MaxFrigateCargos) - { - errorMessage.AppendLine($"{FrigateCargoSubtype} required."); - } - - // Backup check for non-adjacent reactors or cargos - if (allReactorBlocks.Count > reactorCount) - { - errorMessage.AppendLine($"Detected non-adjacent {FrigateReactorSubtype} blocks."); - } - - if (allCargoBlocks.Count > cargoCount) - { - errorMessage.AppendLine($"Detected non-adjacent {FrigateCargoSubtype} blocks."); - } - - return errorMessage.ToString().Trim(); - } - - - - public override void Close() - { - base.Close(); - // Additional cleanup if needed - } - } -} diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/CoreKitGenerator.cs b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/CoreKitGenerator.cs deleted file mode 100644 index 628bbd12..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/CoreKitGenerator.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Sandbox.Definitions; -using Sandbox.Game.Gui; -using Sandbox.Game.Screens.Helpers; -using Sandbox.ModAPI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using VRage.Game; -using VRage.Game.Components; -using VRage.Utils; -using VRageMath; - -namespace TSTSSESCoresAddon.Data.Scripts.ScriptsAddon.customscripts -{ - [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation | MyUpdateOrder.AfterSimulation | MyUpdateOrder.Simulation)] - public class CoreKitGenerator : MySessionComponentBase - { - //public override void LoadData() - //{ - // // Define the block's physical properties - // MyCubeBlockDefinition blockDefinition = new MyCubeBlockDefinition - // { - // Id = new MyDefinitionId(typeof(MyObjectBuilder_CubeBlock), "YourMod_CustomBlock"), - // DisplayNameString = "Custom Block", // Display name - // //Icon = "Path/To/Your/Icon.png", // Path to your block's icon - // CubeSize = MyCubeSize.Large, // Set to MyCubeSize.Small for small grid block - // BlockTopology = MyBlockTopology.TriangleMesh, - // Size = new Vector3I(1f, 1f, 1f), // Size of the block - // GuiVisible = true, - // }; - // - // MyCubeBlockDefinition existingDefinition = null; - // // Add the block to the game - // if (!MyDefinitionManager.Static.TryGetCubeBlockDefinition(blockDefinition.Id, out existingDefinition)) - // { - // MyAPIGateway.Utilities.ShowNotification("" + MyDefinitionManager.Static.Definitions.Definitions.Count); - // MyDefinitionManager.Static.Definitions.AddDefinition(blockDefinition); - // MyAPIGateway.Utilities.ShowNotification("DID THE THING " + blockDefinition.DisplayNameText); - // MyAPIGateway.Utilities.ShowNotification("" + MyDefinitionManager.Static.Definitions.Definitions.Count); - // } - // else - // MyAPIGateway.Utilities.ShowNotification("DID NOT THE THING"); - //} - } -} diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/RemoteInventory.cs b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/RemoteInventory.cs deleted file mode 100644 index 8a89cee4..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/RemoteInventory.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Sandbox.Game.Entities; -using Sandbox.Game.Gui; -using Sandbox.Game.World; -using Sandbox.ModAPI; -using Sandbox.ModAPI.Ingame; -using System; -using System.Collections.Generic; -using System.Linq; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.Entity.UseObject; -using VRage.Game.ModAPI; -using VRage.ModAPI; -using VRageMath; - -namespace Aristeas.RemoteInventory.Data.Scripts -{ - [MySessionComponentDescriptor(MyUpdateOrder.BeforeSimulation | MyUpdateOrder.AfterSimulation | MyUpdateOrder.Simulation)] - public class InventoryGrabber : MySessionComponentBase - { - // TODO replace with core container - const double MaxRange = 100; - const int TimeBetweenInventory = 60; - public static readonly string[] ValidBlockSubtypes = new string[] - { - "FrigateCore_Cargo" - }; - - private Dictionary lockedContainers = null; - - - int lastInventoryTry = 0; - - public override void UpdateBeforeSimulation() - { - base.UpdateBeforeSimulation(); - - //if (lockedContainer != null) - //{ - // MyAPIGateway.Players.GetPlayers(null, player => lockedContainers.ContainsKey(player.PlayerID)); - //} - - if (!MyAPIGateway.Utilities.IsDedicated) - { - // If not currently in GUI screen && Ctrl-R pressed - if (MyAPIGateway.Input.IsKeyPress(VRage.Input.MyKeys.Control) && MyAPIGateway.Input.IsKeyPress(VRage.Input.MyKeys.R) && IsPlayerValid()) { - //if (MyAPIGateway.Gui.GetCurrentScreen == MyTerminalPageEnum.Inventory) - // MyAPIGateway.Gui. - - if (MyAPIGateway.Gui.GetCurrentScreen == MyTerminalPageEnum.None) - { - // Wait between calls, as I suspect there is a large performance implication for this - if (lastInventoryTry > TimeBetweenInventory) - { - GetInventory(); - lastInventoryTry = 0; - } - - } - } - - if (MyAPIGateway.Gui.GetCurrentScreen == MyTerminalPageEnum.None) - MyConstants.DEFAULT_INTERACTIVE_DISTANCE = 5f; - lastInventoryTry++; - } - } - - public void GetInventory() - { - HashSet entities = new HashSet(); - MyAPIGateway.Entities.GetEntities(entities, entity => entity is IMyCubeGrid); - - // Sort entities by distance from player - Vector3D playerPos = MyAPIGateway.Session.Player.GetPosition(); - IOrderedEnumerable sortedEntities = entities.ToList().OrderBy(e => Vector3D.DistanceSquared(e.GetPosition(), playerPos)); - - foreach (var entity in sortedEntities) - { - // Iterates through all entities, closest first. - // If succeeds or out of range, return. - if (Vector3D.Distance(entity.GetPosition(), playerPos) > MaxRange) - break; - - if (TryOpenEntityContainers(entity)) - return; - } - - MyAPIGateway.Utilities.ShowNotification($"No grids in range {MaxRange}m!"); - } - - private bool TryOpenEntityContainers(IMyEntity entity) - { - // Get all CubeGrids and blocks with inventory on them - IMyCubeGrid grid = entity as IMyCubeGrid; - List blocks = new List(); - grid.GetBlocks(blocks, block => block.FatBlock != null && block.FatBlock.HasInventory); - - MyCubeBlock interactBlock = null; - - // Check blocks for ownership - foreach (var block in blocks) - { - if (ValidBlockSubtypes.Length > 0 && !ValidBlockSubtypes.Contains(block.BlockDefinition.Id.SubtypeName)) - continue; - - int relations = (int)block.FatBlock.GetUserRelationToOwner(MyAPIGateway.Session.Player.IdentityId); - // If relations are Owner, Faction, or Neutral - if (relations == 0 || relations == 1 || relations == 2 || relations == 3) - { - interactBlock = (MyCubeBlock)block.FatBlock; - break; - } - } - - if (interactBlock == null) - return false; - - try - { - List useObjects = new List(); - - interactBlock.UseObjectsComponent.GetInteractiveObjects(useObjects); - - if (useObjects.Count > 0) - { - foreach (var useObject in useObjects) - { - // Loop over all actions to look for inventory - if (useObject.PrimaryAction != UseActionEnum.OpenInventory) - continue; - - //MyAPIGateway.Session.Player.Controller.ControlledEntity.Entity.SetPosition(interactBlock.WorldMatrix.Translation); - MyConstants.DEFAULT_INTERACTIVE_DISTANCE = (float)MaxRange; - useObject.Use(UseActionEnum.OpenInventory, MyAPIGateway.Session.Player.Controller.ControlledEntity.Entity); - - return true; - } - } - else - { - MyAPIGateway.Utilities.ShowNotification("NO USEOBJECTS", 5000); - } - } - catch - { - MyAPIGateway.Utilities.ShowNotification("NULL IN INNER METHOD", 5000); - } - - MyAPIGateway.Utilities.ShowNotification($"{grid.CustomName}: {interactBlock?.DisplayNameText}"); - return false; - } - - /// - /// Checks if player exists and is currently in their suit. - /// - /// - private bool IsPlayerValid() - { - return MyAPIGateway.Session.Player != null && - MyAPIGateway.Session.Player.Controller != null && - MyAPIGateway.Session.Player.Controller.ControlledEntity != null && - MyAPIGateway.Session.Player.Controller.ControlledEntity.Entity != null && - MyAPIGateway.Session.Player.Controller.ControlledEntity.Entity is IMyCharacter; - } - } -} diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corekit_script.cs b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corekit_script.cs deleted file mode 100644 index 5ee64186..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corekit_script.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Sandbox.Common.ObjectBuilders; -using Sandbox.ModAPI; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using VRage.Game; -using VRage; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRageMath; -using VRage.ModAPI; -using VRage.ObjectBuilders; -using EmptyKeys.UserInterface.Generated.ContractsBlockView_Gamepad_Bindings; - -namespace TSTSSESCoresAddon.Data.Scripts.ScriptsAddon.customscripts -{ - [MyEntityComponentDescriptor(typeof(MyObjectBuilder_Beacon), false, new string[] { - "CoreKit_1", - })] - public class corekit_script : MyGameLogicComponent - { - private IMyCubeBlock block; - - // TODO: Only trigger block replacement when welded above functional. - public override void Init(MyObjectBuilder_EntityBase objectBuilder) - { - base.Init(objectBuilder); - - block = (IMyCubeBlock)Entity; - - NeedsUpdate |= MyEntityUpdateEnum.BEFORE_NEXT_FRAME; - } - - public override void UpdateOnceBeforeFrame() - { - base.UpdateOnceBeforeFrame(); - - if (block?.CubeGrid?.Physics == null) // ignore projected and other non-physical grids - return; - - NeedsUpdate |= MyEntityUpdateEnum.EACH_FRAME; - } - - - public override void UpdateAfterSimulation() - { - if (!block.IsFunctional) - return; - - MyAPIGateway.Utilities.ShowNotification("KILL MYSELF NOW!"); - var grid = block.CubeGrid; - - grid.RemoveBlock(block.SlimBlock); - - AddBlock(block, "TSTSSES_FrigateCore", block.Position); - } - - private static void AddBlock(IMyCubeBlock block, string subtypeName, Vector3I position) - { - var nextBlockBuilder = new MyObjectBuilder_Beacon - { - SubtypeName = subtypeName, - Min = position, - BlockOrientation = block.Orientation, - ColorMaskHSV = new SerializableVector3(0, -1, 0), - Owner = block.OwnerId, - EntityId = 0, - ShareMode = MyOwnershipShareModeEnum.None - }; - - IMySlimBlock newBlock = block.CubeGrid.AddBlock(nextBlockBuilder, false); - - if (newBlock == null) - { - MyAPIGateway.Utilities.ShowNotification($"Failed to add {subtypeName}", 1000); - return; - } - MyAPIGateway.Utilities.ShowNotification($"{subtypeName} added at {position}", 1000); - } - } -} diff --git a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corerepair.cs.disabled b/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corerepair.cs.disabled deleted file mode 100644 index 926f13af..00000000 --- a/TSTSSESCoresAddon/Data/Scripts/ScriptsAddon/customscripts/corerepair.cs.disabled +++ /dev/null @@ -1,233 +0,0 @@ -using System; -using System.Collections.Generic; -using Sandbox.Common.ObjectBuilders; -using Sandbox.Game; -using Sandbox.Game.Entities; -using Sandbox.Game.EntityComponents; -using Sandbox.ModAPI; -using VRage.Game; -using VRage.Game.Components; -using VRage.Game.ModAPI; -using VRage.Game.ModAPI.Ingame; -using VRage.Game.ModAPI.Network; -using VRage.ModAPI; -using VRage.Network; -using VRage.ObjectBuilders; -using VRage.Sync; -using VRageMath; -using IMySlimBlock = VRage.Game.ModAPI.IMySlimBlock; - -namespace StarCoreCoreRepair -{ - [MyEntityComponentDescriptor(typeof(MyObjectBuilder_Collector), false, "SC_LightCore", "SC_MediumCore", "SC_MediumCore_2x2", "SC_HeavyCore", "SC_HeavyCore_3x3x3", "SC_AdminCore", "SC_SGDroneCore")] - public class StarCoreCoreRepair : MyGameLogicComponent, IMyEventProxy - { - MySync blockDamageModifierSync = null; - private IMyCollector shipCore; - private IMyHudNotification notifStatus = null; - private DateTime repairStartTime; - private TimeSpan repairDelay = TimeSpan.FromSeconds(30); - private bool repairTimerActive = false; - private bool userHasControl = true; // New flag to track if the user has control - - public override void Init(MyObjectBuilder_EntityBase objectBuilder) - { - // Other existing code - //if (!MyAPIGateway.Session.IsServer) return; - shipCore = Entity as IMyCollector; - NeedsUpdate |= MyEntityUpdateEnum.BEFORE_NEXT_FRAME; - SetStatus($"StarCoreCoreRepair Initialized", 5000, MyFontEnum.Green); - - // Attach event handler after API has initialized blockDamageModifierSync - if (blockDamageModifierSync != null) - { - blockDamageModifierSync.ValueChanged += BlockDamageModifierSync_ValueChanged; - } - else - { - // Handle or log that blockDamageModifierSync is null. - } - } - - - - private void BlockDamageModifierSync_ValueChanged(MySync obj) - { - shipCore.SlimBlock.BlockGeneralDamageModifier = obj.Value; - } - - - public override void UpdateOnceBeforeFrame() - { - if (shipCore == null || shipCore.CubeGrid.Physics == null) return; - NeedsUpdate |= MyEntityUpdateEnum.EACH_FRAME; - } - - private bool? lastFunctionalState = null; - private bool allowPowerGeneration = true; - - private int tickCounter = 0; // Add this line to your class fields - private const int TICKS_PER_SECOND = 60; // Assuming 60 ticks per second - - public override void UpdateAfterSimulation() - { - tickCounter++; - if (tickCounter % TICKS_PER_SECOND != 0) - { - return; - } - - if (shipCore == null || shipCore.CubeGrid.Physics == null) return; - - bool isFunctionalNow = shipCore.IsFunctional; - - if (lastFunctionalState != isFunctionalNow) - { - lastFunctionalState = isFunctionalNow; - - if (isFunctionalNow) - { - float newModifier = 1.0f; // New value for BlockGeneralDamageModifier - shipCore.SlimBlock.BlockGeneralDamageModifier = newModifier; - blockDamageModifierSync.Value = newModifier; // Update MySync variable - - SetStatus($"Core is functional.", 2000, MyFontEnum.Green); - repairTimerActive = false; - allowPowerGeneration = true; - userHasControl = true; - } - else - { - float newModifier = 0.01f; // New value for BlockGeneralDamageModifier - shipCore.SlimBlock.BlockGeneralDamageModifier = newModifier; - blockDamageModifierSync.Value = newModifier; // Update MySync variable - - SetStatus($"Core is non-functional. Repair timer started.", 2000, MyFontEnum.Red); - repairStartTime = DateTime.UtcNow; - repairTimerActive = true; - allowPowerGeneration = false; - userHasControl = false; - } - } - - if (repairTimerActive) - { - TimeSpan timeRemaining = repairDelay - (DateTime.UtcNow - repairStartTime); - SetStatus($"Time until core repair: {timeRemaining.TotalSeconds:F0} seconds.", 1000, MyFontEnum.Red); - - if (timeRemaining <= TimeSpan.Zero) - { - DoRepair(); - repairTimerActive = false; - allowPowerGeneration = true; - userHasControl = true; - } - } - - ForceEnabledState(isFunctionalNow); - } - - - - private void ForceEnabledState(bool isFunctional) - { - if (isFunctional) - { - shipCore.Enabled = true; - //SetStatus($"Core forced ON due to functionality.", 2000, MyFontEnum.Green); - TogglePowerGenerationBlocks(true); - } - else - { - shipCore.Enabled = false; - //SetStatus($"Core forced OFF due to non-functionality.", 2000, MyFontEnum.Red); - TogglePowerGenerationBlocks(false); - } - } - - private void TogglePowerGenerationBlocks(bool enable) - { - var blocks = new List(); - shipCore.CubeGrid.GetBlocks(blocks); - - foreach (var block in blocks) - { - var functionalBlock = block.FatBlock as IMyFunctionalBlock; - - if (functionalBlock != null && functionalBlock != shipCore) - { - // Check if the block is a battery or reactor - if (functionalBlock is IMyBatteryBlock || functionalBlock is IMyReactor) - { - if (userHasControl) // Check if the user has control over the power blocks - { - functionalBlock.Enabled = enable; - } - else // If not, forcibly turn off the power blocks - { - functionalBlock.Enabled = false; - } - } - } - } - //string statusMessage = enable ? "enabled" : "forced off"; - //SetStatus($"All power generation blocks on grid {statusMessage}.", 2000, enable ? MyFontEnum.Green : MyFontEnum.Red); - } - - - - - // Add this field to your class to store the original owner ID. - - private void DoRepair() - { - if (shipCore == null || shipCore.CubeGrid.Physics == null) return; - - IMySlimBlock slimBlock = shipCore.SlimBlock; - if (slimBlock == null) return; - - // Fetch the original owner ID of the grid. - long gridOwnerId = shipCore.CubeGrid.BigOwners.Count > 0 ? shipCore.CubeGrid.BigOwners[0] : 0; - - // If the grid has an owner, proceed with repair and ownership change. - if (gridOwnerId != 0) - { - float repairAmount = 9999; - slimBlock.IncreaseMountLevel(repairAmount, 0L, null, 0f, false, MyOwnershipShareModeEnum.Faction); - - // Try casting to MyCubeBlock and change the owner. - MyCubeBlock cubeBlock = shipCore as MyCubeBlock; - if (cubeBlock != null) - { - cubeBlock.ChangeBlockOwnerRequest(gridOwnerId, MyOwnershipShareModeEnum.Faction); - } - - SetStatus($"Core repaired.", 2000, MyFontEnum.Green); - } - else - { - // Handle the case where the grid has no owner. - SetStatus($"Core could not be repaired: Grid has no owner.", 2000, MyFontEnum.Red); - } - } - - - - private void SetStatus(string text, int aliveTime = 300, string font = MyFontEnum.Green) - { - if (notifStatus == null) - notifStatus = MyAPIGateway.Utilities.CreateNotification("", aliveTime, font); - - notifStatus.Hide(); - notifStatus.Font = font; - notifStatus.Text = text; - notifStatus.AliveTime = aliveTime; - notifStatus.Show(); - } - - public override void Close() - { - // Cleanup logic here, if necessary - } - } -} diff --git a/readme.txt b/readme.txt new file mode 100644 index 00000000..d62ea244 --- /dev/null +++ b/readme.txt @@ -0,0 +1 @@ +this is a version of the cores addon from ~october 2023 where I tried to make"CoreKits". I successfully made an arbitrary frigate/destroyer core and destroyer/frigate cargos that detect stuff around it and warn you. We now have a framework for that called Modular Assemblies. also included is a weird remote inventory experiment. \ No newline at end of file diff --git a/video games.txt b/video games.txt deleted file mode 100644 index e69de29b..00000000