diff --git a/Client/API/IClientEventAPI.cs b/Client/API/IClientEventAPI.cs index d9c2c4af..1efd11a0 100644 --- a/Client/API/IClientEventAPI.cs +++ b/Client/API/IClientEventAPI.cs @@ -55,6 +55,7 @@ public interface IAsyncParticleManager /// /// Return false to stop spawning particles /// + /// /// /// public delegate bool ContinousParticleSpawnTaskDelegate(float dt, IAsyncParticleManager manager); @@ -189,6 +190,7 @@ public interface IClientEventAPI : IEventAPI /// /// Registers a custom itemstack renderer for given collectible object. If none is registered, the default renderer is used. For render target gui, the gui shader and its uniforms are already fully prepared, you may only call RenderMesh() and ignore the modelMat, position and size values - stack sizes however, are not covered by this. /// + /// /// /// void RegisterItemstackRenderer(CollectibleObject forObj, ItemRenderDelegate rendererDelegate, EnumItemRenderTarget target); @@ -197,6 +199,7 @@ public interface IClientEventAPI : IEventAPI /// Removes a previously registered itemstack renderer /// /// + /// void UnregisterItemstackRenderer(CollectibleObject forObj, EnumItemRenderTarget target); diff --git a/Client/API/IClientWorldAccessor.cs b/Client/API/IClientWorldAccessor.cs index fdab9204..24d0efd9 100644 --- a/Client/API/IClientWorldAccessor.cs +++ b/Client/API/IClientWorldAccessor.cs @@ -3,6 +3,7 @@ using Vintagestory.API.Common; using Vintagestory.API.Common.Entities; using Vintagestory.API.MathTools; +using Vintagestory.API.Server; namespace Vintagestory.API.Client { @@ -142,8 +143,9 @@ public interface IClientWorldAccessor : IWorldAccessor /// /// Same effect as when player left-click breaks a block, but will not cause actual breakage of the block /// - /// + /// /// + /// void IncurBlockDamage(BlockSelection blockSelection, EnumTool? withTool, float damage); /// @@ -167,5 +169,10 @@ public interface IClientWorldAccessor : IWorldAccessor Dictionary Dimensions { get; } IMiniDimension GetOrCreateDimension(int dimId, Vec3d pos); bool TryGetMiniDimension(Vec3i origin, out IMiniDimension dimension); + + /// + /// Exactly like PlaySoundAt except that it returns the duration of the played sound. (We don't want to change the method signature of PlaySoundAt for API mod breakage reasons) + /// + int PlaySoundAtAndGetDuration(AssetLocation sound, double x, double y, double z, IPlayer ignorePlayerUid = null, bool randomizePitch = true, float range = 32, float volume = 1f); } } diff --git a/Client/API/ICoreClientAPI.cs b/Client/API/ICoreClientAPI.cs index d3656beb..d5d98459 100644 --- a/Client/API/ICoreClientAPI.cs +++ b/Client/API/ICoreClientAPI.cs @@ -188,7 +188,9 @@ public interface ICoreClientAPI : ICoreAPI /// /// Triggers a discovery event. HudDiscoveryMessage registers to this event and fades in/out a "discovery message" on the players screen /// - /// + /// + /// + /// void TriggerIngameDiscovery(object sender, string errorCode, string text); /// diff --git a/Client/API/IInputAPI.cs b/Client/API/IInputAPI.cs index 14d7b293..58475de3 100644 --- a/Client/API/IInputAPI.cs +++ b/Client/API/IInputAPI.cs @@ -8,6 +8,13 @@ public class MouseButtonState public bool Left; public bool Middle; public bool Right; + + public void Clear() + { + Left = false; + Middle = false; + Right = false; + } } public delegate void OnHotKeyDelegate(string hotkeycode, KeyCombination keyComb); diff --git a/Client/API/IRenderAPI.cs b/Client/API/IRenderAPI.cs index 8212d2f3..32ec195d 100644 --- a/Client/API/IRenderAPI.cs +++ b/Client/API/IRenderAPI.cs @@ -91,6 +91,7 @@ public interface IRenderAPI /// /// /// + /// /// ItemRenderInfo GetItemStackRenderInfo(ItemSlot inSlot, EnumItemRenderTarget ground, float dt); @@ -279,7 +280,7 @@ float[] CurrentShadowProjectionMatrix /// Whether scissor mode should be active or not /// /// - void GlScissorFlag(bool enable); + void GlScissorFlag(bool enable); @@ -429,6 +430,7 @@ float[] CurrentShadowProjectionMatrix /// The position for light level reading /// The position for light level reading /// The position for light level reading + /// /// IStandardShaderProgram PreparedStandardShader(int posX, int posY, int posZ, Vec4f colorMul = null); @@ -452,6 +454,7 @@ float[] CurrentShadowProjectionMatrix /// Size of the indices /// Float values of the mesh /// Float values of the mesh + /// /// Byte values of the mesh /// The current draw mode /// whether the draw should be static or dynamic. @@ -507,13 +510,13 @@ float[] CurrentShadowProjectionMatrix /// /// /// + /// void RenderMultiTextureMesh(MultiTextureMeshRef mmr, string textureSampleName, int textureNumber = 0); /// /// Renders given mesh onto the screen, with the mesh requiring multiple render calls for each texture, does not asign any texture /// /// - /// void RenderMultiTextureMesh(MultiTextureMeshRef mmr); @@ -598,6 +601,9 @@ float[] CurrentShadowProjectionMatrix /// /// /// Once rendered, this returns a texture subid, which you can use to retrieve the textureAtlasPosition from the atlas + /// + /// + /// /// True if the render could complete immediatly, false if it has to wait until the next ortho render stage bool RenderItemStackToAtlas(ItemStack stack, ITextureAtlasAPI atlas, int size, Action onComplete, int color = ColorUtil.WhiteArgb, float sepiaLevel = 0f, float scale = 1f); @@ -680,17 +686,16 @@ float[] CurrentShadowProjectionMatrix void Render2DTexture(int textureid, float posX, float posY, float width, float height, float z = 50, Vec4f color = null); - /// /// Renders given texture onto the screen, uses supplied quad for rendering (gui mode) /// + /// /// /// /// /// /// /// - /// void Render2DTexture(MeshRef quadModel, int textureid, float posX, float posY, float width, float height, float z = 50); @@ -727,6 +732,7 @@ float[] CurrentShadowProjectionMatrix /// /// Inefficiently renders a line between 2 points /// + /// /// /// /// diff --git a/Client/API/ITesselatorAPI.cs b/Client/API/ITesselatorAPI.cs index f05588c3..0a7042fb 100644 --- a/Client/API/ITesselatorAPI.cs +++ b/Client/API/ITesselatorAPI.cs @@ -184,6 +184,8 @@ void TesselateShape( /// /// /// + /// + /// void TesselateShapeWithJointIds(string typeForLogging, Shape shapeBase, out MeshData modeldata, ITexPositionSource texSource, Vec3f rotation, int? quantityElements = null, string[] selectiveElements = null); /// diff --git a/Client/API/ITextureAtlasAPI.cs b/Client/API/ITextureAtlasAPI.cs index 04e5ff56..1cc80c07 100644 --- a/Client/API/ITextureAtlasAPI.cs +++ b/Client/API/ITextureAtlasAPI.cs @@ -17,6 +17,7 @@ public interface IItemTextureAtlasAPI : ITextureAtlasAPI /// /// /// + /// /// TextureAtlasPosition GetPosition(Item item, string textureName = null, bool returnNullWhenMissing = false); @@ -32,6 +33,7 @@ public interface IBlockTextureAtlasAPI : ITextureAtlasAPI /// /// /// + /// /// TextureAtlasPosition GetPosition(Block block, string textureName, bool returnNullWhenMissing = false); @@ -92,6 +94,7 @@ public interface ITextureAtlasAPI /// /// /// + /// /// bool InsertTexture(IBitmap bmp, out int textureSubId, out TextureAtlasPosition texPos, float alphaTest = 0f); @@ -99,9 +102,10 @@ public interface ITextureAtlasAPI /// Inserts a texture into the texture atlas after the atlas has been generated. Updates the in-ram texture atlas as well as the in-gpu-ram texture atlas. /// The textureSubId can be used to find the TextureAtlasPosition again in case you loose it ;-) /// - /// + /// /// /// + /// /// bool InsertTexture(byte[] pngBytes, out int textureSubId, out TextureAtlasPosition texPos, float alphaTest = 0f); @@ -143,7 +147,7 @@ public interface ITextureAtlasAPI /// A subsequent call to this method will update the texture, but retain the same texPos. Also a run-time texture reload will reload this texture automatically. /// /// - /// + /// /// /// /// @@ -185,7 +189,7 @@ public interface ITextureAtlasAPI /// /// Returns one of 30 random rgba values inside the given texture (defined by its sub-id) /// - /// + /// /// 0..29 for a specific random pixel, or -1 to randomize, which is the same as calling GetRandomColor without the rndIndex argument /// int GetRandomColor(TextureAtlasPosition texPos, int rndIndex); @@ -207,6 +211,7 @@ public interface ITextureAtlasAPI /// /// Renders given texture into the texture atlas at given location /// + /// /// /// /// diff --git a/Client/Audio/CaveMusicTrack.cs b/Client/Audio/CaveMusicTrack.cs index 21c4d805..05e5cd9d 100644 --- a/Client/Audio/CaveMusicTrack.cs +++ b/Client/Audio/CaveMusicTrack.cs @@ -101,6 +101,7 @@ float IMusicTrack.Priority /// /// the global Asset Manager /// The Core Client API + /// public void Initialize(IAssetManager assetManager, ICoreClientAPI capi, IMusicEngine musicEngine) { this.capi = capi; @@ -124,6 +125,8 @@ public void Initialize(IAssetManager assetManager, ICoreClientAPI capi, IMusicEn /// Should the game play this track? /// /// The properties of the current track. + /// + /// /// Do we play this track? public bool ShouldPlay(TrackedPlayerProperties props, ClimateCondition conds, BlockPos pos) { diff --git a/Client/Audio/IMusicTrack.cs b/Client/Audio/IMusicTrack.cs index 70a9006d..5cca00ec 100644 --- a/Client/Audio/IMusicTrack.cs +++ b/Client/Audio/IMusicTrack.cs @@ -37,12 +37,15 @@ public interface IMusicTrack /// /// the global Asset Manager /// The Core Client API + /// void Initialize(IAssetManager assetManager, ICoreClientAPI capi, IMusicEngine musicEngine); /// /// Should this current track play? /// /// Player Properties + /// + /// /// Should we play the current track? bool ShouldPlay(TrackedPlayerProperties props, ClimateCondition conds, BlockPos pos); diff --git a/Client/Audio/MusicTrack.cs b/Client/Audio/MusicTrack.cs index 34892af8..780a1535 100644 --- a/Client/Audio/MusicTrack.cs +++ b/Client/Audio/MusicTrack.cs @@ -82,6 +82,8 @@ public virtual void Initialize(IAssetManager assetManager, ICoreClientAPI capi, /// Should this current track play? /// /// Player Properties + /// + /// /// Should we play the current track? public virtual bool ShouldPlay(TrackedPlayerProperties props, ClimateCondition conds, BlockPos pos) { diff --git a/Client/MeshPool/MeshDataPool.cs b/Client/MeshPool/MeshDataPool.cs index d7421e3f..7bdddfdc 100644 --- a/Client/MeshPool/MeshDataPool.cs +++ b/Client/MeshPool/MeshDataPool.cs @@ -99,6 +99,7 @@ private MeshDataPool(int verticesPoolSize, int indicesPoolSize, int maxPartsPerP /// The index pool size. /// The maximum parts per pool. /// The custom floats of the pool. + /// /// The custom bytes of the pool. /// The custom ints of the pool. /// The resulting mesh data pool. @@ -155,6 +156,7 @@ public static MeshDataPool AllocateNewPool(ICoreClientAPI capi, int verticesPool /// The core client API /// The model to add /// The origin point of the model. + /// /// The culling sphere. /// The location of the model (and the data) in the pool. public ModelDataPoolLocation TryAdd(ICoreClientAPI capi, MeshData modeldata, Vec3i modelOrigin, int dimension, Sphere frustumCullSphere) @@ -442,6 +444,11 @@ public float GetFragmentation() { return CurrentFragmentation; } + + public void RenderMesh(IRenderAPI render) + { + render.RenderMesh(modelRef, indicesStartsByte, indicesSizes, indicesGroupsCount); + } } diff --git a/Client/MeshPool/MeshDataPoolManager.cs b/Client/MeshPool/MeshDataPoolManager.cs index b1928393..23e675a9 100644 --- a/Client/MeshPool/MeshDataPoolManager.cs +++ b/Client/MeshPool/MeshDataPoolManager.cs @@ -38,6 +38,7 @@ public class MeshDataPoolManager /// Size allocated for the Indices /// The maximum number of parts for this pool. /// Additional float data + /// /// Additional byte data /// additional int data public MeshDataPoolManager(MeshDataPoolMasterManager masterPool, FrustumCulling frustumCuller, ICoreClientAPI capi, int defaultVertexPoolSize, int defaultIndexPoolSize, int maxPartsPerPool, CustomMeshDataPartFloat customFloats = null, CustomMeshDataPartShort customShorts = null, CustomMeshDataPartByte customBytes = null, CustomMeshDataPartInt customInts = null) @@ -59,6 +60,7 @@ public MeshDataPoolManager(MeshDataPoolMasterManager masterPool, FrustumCulling /// /// The model data /// The origin point of the Model + /// /// The culling sphere. /// The location identifier for the pooled model. public ModelDataPoolLocation AddModel(MeshData modeldata, Vec3i modelOrigin, int dimension, Sphere frustumCullSphere) @@ -99,7 +101,7 @@ public ModelDataPoolLocation AddModel(MeshData modeldata, Vec3i modelOrigin, int } /// - /// Renders the model. + /// Renders the chunk models to the GPU. One of the most important methods in the entire game! /// /// The position of the Player /// @@ -111,6 +113,7 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod { bool revertModelviewMatrix = false; bool revertMVPMatrix = false; + bool revertTransparency = false; MeshDataPool pool = pools[i]; if (pool.dimensionId == Dimensions.MiniDimensions) { @@ -130,6 +133,11 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod { currShader.UniformMatrix("modelViewMatrix", dimension.GetRenderTransformMatrix(masterPool.currentModelViewMatrix, playerpos)); revertModelviewMatrix = true; + if (currShader.HasUniform("forcedTransparency")) + { + currShader.Uniform("forcedTransparency", capi.Settings.Float["previewTransparency"]); + revertTransparency = true; + } } else if (currShader.HasUniform("mvpMatrix")) { @@ -142,6 +150,20 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod (float)(pool.poolOrigin.Y + renderOffset.Y - playerpos.Y), (float)(pool.poolOrigin.Z + renderOffset.Z - playerpos.Z) )); + + try + { + pool.RenderMesh(capi.Render); + } + finally + { + if (revertModelviewMatrix) + capi.Render.CurrentActiveShader.UniformMatrix("modelViewMatrix", masterPool.currentModelViewMatrix); + if (revertMVPMatrix) + capi.Render.CurrentActiveShader.UniformMatrix("mvpMatrix", masterPool.shadowMVPMatrix); + if (revertTransparency) + capi.Render.CurrentActiveShader.Uniform("forcedTransparency", 0f); + } } else { @@ -157,14 +179,9 @@ public void Render(Vec3d playerpos, string originUniformName, EnumFrustumCullMod (float)(pool.poolOrigin.Y - playerpos.Y), (float)(pool.poolOrigin.Z - playerpos.Z) )); - } - capi.Render.RenderMesh(pool.modelRef, pool.indicesStartsByte, pool.indicesSizes, pool.indicesGroupsCount); - - if (revertModelviewMatrix) - capi.Render.CurrentActiveShader.UniformMatrix("modelViewMatrix", masterPool.currentModelViewMatrix); - if (revertMVPMatrix) - capi.Render.CurrentActiveShader.UniformMatrix("mvpMatrix", masterPool.shadowMVPMatrix); + pool.RenderMesh(capi.Render); + } } } diff --git a/Client/Model/Mesh/CubeMeshUtil.cs b/Client/Model/Mesh/CubeMeshUtil.cs index 5a07f14c..71a390db 100644 --- a/Client/Model/Mesh/CubeMeshUtil.cs +++ b/Client/Model/Mesh/CubeMeshUtil.cs @@ -181,6 +181,7 @@ public static MeshData GetCube() m.SetRgba(rgba); m.SetUv(uv); + m.TextureIndices = new byte[6]; m.SetVerticesCount(4 * 6); m.SetIndices(CubeVertexIndices); m.SetIndicesCount(3 * 2 * 6); diff --git a/Client/Model/Mesh/CustomMeshDataPart.cs b/Client/Model/Mesh/CustomMeshDataPart.cs index ad318413..d910219b 100644 --- a/Client/Model/Mesh/CustomMeshDataPart.cs +++ b/Client/Model/Mesh/CustomMeshDataPart.cs @@ -193,7 +193,7 @@ public void SetFrom(CustomMeshDataPart meshdatapart) /// /// Sets a value from a given mesh data part. /// - /// the mesh data part for this type. + /// the mesh data part for this type. protected CustomMeshDataPart EmptyClone(CustomMeshDataPart cloned) { cloned.customAllocationSize = customAllocationSize; diff --git a/Client/Model/Mesh/CustomMeshDataPartByte.cs b/Client/Model/Mesh/CustomMeshDataPartByte.cs index 7db597d0..688d8dfa 100644 --- a/Client/Model/Mesh/CustomMeshDataPartByte.cs +++ b/Client/Model/Mesh/CustomMeshDataPartByte.cs @@ -1,6 +1,4 @@ -using System; - -namespace Vintagestory.API.Client +namespace Vintagestory.API.Client { public enum DataConversion { diff --git a/Client/Model/Mesh/CustomMeshDataPartInt.cs b/Client/Model/Mesh/CustomMeshDataPartInt.cs index d911f63b..2034f09d 100644 --- a/Client/Model/Mesh/CustomMeshDataPartInt.cs +++ b/Client/Model/Mesh/CustomMeshDataPartInt.cs @@ -1,6 +1,4 @@ -using System; - -namespace Vintagestory.API.Client +namespace Vintagestory.API.Client { /// /// Holds arbitrary int data for meshes to be used in the shader @@ -15,7 +13,7 @@ public CustomMeshDataPartInt() : base() { } /// /// Size initialization constructor. /// - /// + /// public CustomMeshDataPartInt(int size) : base(size) { } public DataConversion Conversion = DataConversion.Integer; diff --git a/Client/Model/Mesh/CustomMeshDataPartShort.cs b/Client/Model/Mesh/CustomMeshDataPartShort.cs index 5431f253..a33586cd 100644 --- a/Client/Model/Mesh/CustomMeshDataPartShort.cs +++ b/Client/Model/Mesh/CustomMeshDataPartShort.cs @@ -1,6 +1,4 @@ -using System; - -namespace Vintagestory.API.Client +namespace Vintagestory.API.Client { /// /// Holds arbitrary short data for meshes to be used in the shader @@ -17,7 +15,7 @@ public CustomMeshDataPartShort() : base() { } /// /// Size initialization constructor. /// - /// + /// public CustomMeshDataPartShort(int size) : base(size) { } diff --git a/Client/Model/Mesh/IMeshPoolSupplier.cs b/Client/Model/Mesh/IMeshPoolSupplier.cs index 99ff748d..67ade6fa 100644 --- a/Client/Model/Mesh/IMeshPoolSupplier.cs +++ b/Client/Model/Mesh/IMeshPoolSupplier.cs @@ -5,6 +5,7 @@ public interface IMeshPoolSupplier /// /// Gets a mesh pool supplier for the given render pass. /// + /// /// The given render pass. /// /// The mesh data for the render pass. diff --git a/Client/Model/Mesh/MeshData.cs b/Client/Model/Mesh/MeshData.cs index 4cb07687..b77ec819 100644 --- a/Client/Model/Mesh/MeshData.cs +++ b/Client/Model/Mesh/MeshData.cs @@ -3,7 +3,6 @@ using Vintagestory.API.Common; using Vintagestory.API.MathTools; using Vintagestory.API.Util; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Vintagestory.API.Client { @@ -1620,7 +1619,7 @@ public MeshData Clone() if (RenderPassesAndExtraBits != null) { - dest.RenderPassesAndExtraBits = RenderPasses.FastCopy(RenderPassCount); + dest.RenderPassesAndExtraBits = RenderPassesAndExtraBits.FastCopy(RenderPassCount); dest.RenderPassCount = RenderPassCount; } diff --git a/Client/Model/Mesh/MeshUtil.cs b/Client/Model/Mesh/MeshUtil.cs index 303bf81d..49fced98 100644 --- a/Client/Model/Mesh/MeshUtil.cs +++ b/Client/Model/Mesh/MeshUtil.cs @@ -56,7 +56,7 @@ public static void ToggleWindModeSetWindData(this MeshData sourceMesh, int leave // We add the ground offset to the winddatabits, but not if this side of the block is flagged in leavesNoShearTileSide (because against a solid block) - in that case, ground offset will remain zero for (int vertexNum = 0; vertexNum < verticesCount; vertexNum++) { - int flag = sourceMesh.Flags[vertexNum] &= VertexFlags.ClearWindDataBitsMask; + int groundOffset = 0; float fx = sourceMesh.xyz[vertexNum * 3 + 0]; float fz = sourceMesh.xyz[vertexNum * 3 + 2]; @@ -72,10 +72,10 @@ public static void ToggleWindModeSetWindData(this MeshData sourceMesh, int leave // Every vertex has three flags set, because all vertices are on the "outside" of a leaves block - yes, a leaves block is not a standard cube and it has probably been rotated, but this is a good enough approximation to whether this vertex is close to the solid neighbour or not if ((leavesNoShearTileSide & sidesToCheckMask) == 0) { - flag |= (groundOffsetTop == 8 ? 7 : groundOffsetTop + y) << VertexFlags.WindDataBitsPos; + groundOffset = groundOffsetTop == 8 ? 7 : groundOffsetTop + y; } - sourceMesh.Flags[vertexNum] = flag; + VertexFlags.ReplaceWindData(ref sourceMesh.Flags[vertexNum], groundOffset); } } diff --git a/Client/Model/Tesselation/TileSide.cs b/Client/Model/Tesselation/TileSide.cs index 57834425..604fbded 100644 --- a/Client/Model/Tesselation/TileSide.cs +++ b/Client/Model/Tesselation/TileSide.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Vintagestory.API.MathTools; namespace Vintagestory.API.Client.Tesselation { @@ -23,6 +24,16 @@ public class TileSideEnum 2, 0, 2, 0, 1, 1 }; + public static FastVec3i[] OffsetByTileSide = new FastVec3i[] + { + new FastVec3i(0,0,-1), + new FastVec3i(1,0,0), + new FastVec3i(0,0,1), + new FastVec3i(-1,0,0), + new FastVec3i(0,1,0), + new FastVec3i(0,-1,0) + }; + public static int[] MoveIndex = new int[6]; public static string[] Codes = new string[] { "north", "east", "south", "west", "up", "down" }; diff --git a/Client/Render/EnumShaderProgram.cs b/Client/Render/EnumShaderProgram.cs index b2a3d176..c8a1ab38 100644 --- a/Client/Render/EnumShaderProgram.cs +++ b/Client/Render/EnumShaderProgram.cs @@ -115,10 +115,6 @@ public enum EnumShaderProgram Colorgrade = 37, Guigear = 38, Ssao = 39, - Bilateralblur = 40, - Grass = 41, - Flowers = 42, - Shadowgrass = 43, - Shadowflowers = 44 + Bilateralblur = 40 } } diff --git a/Client/Texture/CompositeTexture.cs b/Client/Texture/CompositeTexture.cs index 84fdca41..e7f80292 100644 --- a/Client/Texture/CompositeTexture.cs +++ b/Client/Texture/CompositeTexture.cs @@ -357,6 +357,8 @@ static BakedCompositeTexture Bake(IAssetManager assetManager, CompositeTexture c var tile = ct.Tiles[i]; if (tile.Base.EndsWithWildCard) { + if (wildcardsCache == null) wildcardsCache = new Dictionary>(); + // Fix borked windows sorting (i.e. 1, 10, 11, 12, ....) var basePath = ct.Base.Path.Substring(0, ct.Base.Path.Length - 1); var assets = wildcardsCache[ct.Base] = assetManager.GetManyInCategory("textures", basePath, ct.Base.Domain); diff --git a/Client/Texture/TextureAtlasPosition.cs b/Client/Texture/TextureAtlasPosition.cs index b1ddd3f8..104fc0d2 100644 --- a/Client/Texture/TextureAtlasPosition.cs +++ b/Client/Texture/TextureAtlasPosition.cs @@ -1,6 +1,4 @@ -using System; - -namespace Vintagestory.API.Client +namespace Vintagestory.API.Client { /// /// The position of a texture inside an atlas diff --git a/Client/UI/CairoFont.cs b/Client/UI/CairoFont.cs index e4cde302..a11f9fdb 100644 --- a/Client/UI/CairoFont.cs +++ b/Client/UI/CairoFont.cs @@ -100,6 +100,7 @@ public CairoFont(double unscaledFontSize, string fontName, double[] color, doubl /// /// The text of the object. /// The bounds of the element where the font is displayed. + /// public void AutoFontSize(string text, ElementBounds bounds, bool onlyShrink = true) { var origsize = UnscaledFontsize; diff --git a/Client/UI/Dialog/GuiDialog.cs b/Client/UI/Dialog/GuiDialog.cs index eed5ba8d..1eb2c33b 100644 --- a/Client/UI/Dialog/GuiDialog.cs +++ b/Client/UI/Dialog/GuiDialog.cs @@ -264,6 +264,11 @@ public virtual void OnGuiClosed() { /// /// Was this dialogue successfully opened? public virtual bool TryOpen() + { + return TryOpen(true); + } + + public virtual bool TryOpen(bool withFocus) { bool wasOpened = opened; @@ -273,7 +278,7 @@ public virtual bool TryOpen() } opened = true; - if (DialogType == EnumDialogType.Dialog) + if (DialogType == EnumDialogType.Dialog && withFocus) { capi.Gui.RequestFocus(this); } diff --git a/Client/UI/Dialog/GuiDialogBlockEntity.cs b/Client/UI/Dialog/GuiDialogBlockEntity.cs index 8608c5dc..251293ce 100644 --- a/Client/UI/Dialog/GuiDialogBlockEntity.cs +++ b/Client/UI/Dialog/GuiDialogBlockEntity.cs @@ -58,7 +58,6 @@ public GuiDialogBlockEntity(string dialogTitle, InventoryBase inventory, BlockPo /// The title of this dialogue. Ex: "Chest" - /// The inventory associated with this block entity. /// The position of this block entity. /// The Client API public GuiDialogBlockEntity(string dialogTitle, BlockPos blockEntityPos, ICoreClientAPI capi) diff --git a/Client/UI/Dialog/GuiJsonDialog.cs b/Client/UI/Dialog/GuiJsonDialog.cs index 20436b69..3f554c5e 100644 --- a/Client/UI/Dialog/GuiJsonDialog.cs +++ b/Client/UI/Dialog/GuiJsonDialog.cs @@ -31,14 +31,26 @@ public override string ToggleKeyCombinationCode } /// - /// Builds the dialogue using the dialogue settings from JSON. + /// Builds the dialog using the dialog settings from JSON. /// - /// The dialogue settings. + /// The dialog settings. /// The Client API public GuiJsonDialog(JsonDialogSettings settings, ICoreClientAPI capi) : base("", capi) { this.settings = settings; - ComposeDialog(); + ComposeDialog(true); + } + + /// + /// Builds the dialog using the dialog settings from JSON. + /// + /// The dialog settings. + /// The Client API + /// Should the first element be focused, after building the dialog? + public GuiJsonDialog(JsonDialogSettings settings, ICoreClientAPI capi, bool focusFirstElement) : base("", capi) + { + this.settings = settings; + ComposeDialog(focusFirstElement); } /// @@ -46,7 +58,7 @@ public GuiJsonDialog(JsonDialogSettings settings, ICoreClientAPI capi) : base("" /// public override void Recompose() { - ComposeDialog(); + ComposeDialog(false); } public override bool PrefersUngrabbedMouse => settings.DisableWorldInteract; @@ -54,7 +66,7 @@ public override void Recompose() /// /// Composes the dialogue with specifications dictated by JSON. /// - public void ComposeDialog() + public void ComposeDialog(bool focusFirstElement = false) { double factor = settings.SizeMultiplier; @@ -101,7 +113,7 @@ public void ComposeDialog() } - Composers["cmdDlg" + settings.Code] = composer.EndChildElements().Compose(); + Composers["cmdDlg" + settings.Code] = composer.EndChildElements().Compose(focusFirstElement); } int elementNumber = 0; diff --git a/Client/UI/Elements/Impl/GuiElement.cs b/Client/UI/Elements/Impl/GuiElement.cs index 8d1d55cc..1a5565af 100644 --- a/Client/UI/Elements/Impl/GuiElement.cs +++ b/Client/UI/Elements/Impl/GuiElement.cs @@ -329,6 +329,8 @@ public virtual void BeforeCalcBounds() /// The Client API /// The name of the file. /// Do we cache the file? + /// + /// /// The resulting surface pattern. public static SurfacePattern getPattern(ICoreClientAPI capi, AssetLocation textureLoc, bool doCache = true, int mulAlpha = 255, float scale = 1) { @@ -359,6 +361,7 @@ public static SurfacePattern getPattern(ICoreClientAPI capi, AssetLocation textu /// /// The Client API /// The name of the text file. + /// /// public unsafe static ImageSurface getImageSurfaceFromAsset(ICoreClientAPI capi, AssetLocation textureLoc, int mulAlpha = 255) { @@ -394,7 +397,10 @@ public unsafe static ImageSurface getImageSurfaceFromAsset(ICoreClientAPI capi, /// The Client API /// The context of the fill. /// The name of the texture file. + /// /// Whether or not to preserve the aspect ratio of the texture. + /// + /// /// The surface pattern filled with the given texture. public static SurfacePattern fillWithPattern(ICoreClientAPI capi, Context ctx, AssetLocation textureLoc, bool nearestScalingFiler = false, bool preserve = false, int mulAlpha = 255, float scale = 1f) { @@ -602,7 +608,6 @@ public void EmbossRoundRectangleElement(Context ctx, ElementBounds bounds, bool /// The radius of the corner of the rectangle. /// The thickness of the emboss. (Default: 3) /// The intensity of the emboss. (Default: 0.4f) - /// How quickly the effect falls off around corners (default: 2) /// How skewed is the light/dark balance (Default: 1) /// Whether or not it goes in or out. (Default: false) /// The offset for the alpha part of the emboss. (Default: 0) @@ -689,7 +694,7 @@ public virtual void OnMouseDown(ICoreClientAPI api, MouseEvent mouse) /// The event fired when the mouse is pressed while on the element. Called after OnMouseDown and tells the engine that the event is handled. /// /// The Client API - /// The mouse event args. + /// The mouse event args. public virtual void OnMouseDownOnElement(ICoreClientAPI api, MouseEvent args) { args.Handled = true; @@ -699,7 +704,7 @@ public virtual void OnMouseDownOnElement(ICoreClientAPI api, MouseEvent args) /// The event fired when the mouse is released on the element. Called after OnMouseUp. /// /// The Client API - /// The mouse event args. + /// The mouse event args. public virtual void OnMouseUpOnElement(ICoreClientAPI api, MouseEvent args) { } /// @@ -769,7 +774,7 @@ public virtual void OnKeyPress(ICoreClientAPI api, KeyEvent args) { } /// public virtual bool IsPositionInside(int posX, int posY) { - return (InsideClipBounds == null || InsideClipBounds.PointInside(posX, posY)) && Bounds.PointInside(posX, posY); + return Bounds.PointInside(posX, posY) && (InsideClipBounds == null || InsideClipBounds.PointInside(posX, posY)); } public virtual string MouseOverCursor { get; protected set; } = null; diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementCompactScrollbar.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementCompactScrollbar.cs index 9e4bf88a..9ceab609 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementCompactScrollbar.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementCompactScrollbar.cs @@ -84,10 +84,10 @@ public override void RenderInteractiveElements(float deltaTime) public static partial class GuiComposerHelpers { - /// /// Adds a compact vertical scrollbar to the current GUI. /// + /// /// The event fired for the change in the scrollbar. /// the bounds of the scrollbar. /// the internal name of the scrollbar. @@ -103,6 +103,7 @@ public static GuiComposer AddCompactVerticalScrollbar(this GuiComposer composer, /// /// Gets the scrollbar from the dialogue. /// + /// /// the internal name of the scrollbar to be gotten /// The scrollbar with the given key. public static GuiElementCompactScrollbar GetCompactScrollbar(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementDropDown.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementDropDown.cs index 25d006a0..efbd7882 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementDropDown.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementDropDown.cs @@ -101,6 +101,8 @@ public override bool Enabled { /// The default selected index. /// The event that occurs when the selection is changed. /// The bounds of the drop down. + /// + /// public GuiElementDropDown(ICoreClientAPI capi, string[] values, string[] names, int selectedIndex, SelectionChangedDelegate onSelectionChanged, ElementBounds bounds, CairoFont font, bool multiSelect) : base(capi, "", font, bounds) { highlightTexture = new LoadedTexture(capi); @@ -458,6 +460,7 @@ public static partial class GuiComposerHelpers /// /// Adds a multiple select dropdown to the current GUI instance. /// + /// /// The values of the current drodown. /// The names of those values. /// The default selected index. @@ -474,10 +477,10 @@ public static GuiComposer AddMultiSelectDropDown(this GuiComposer composer, stri } - /// /// Adds a dropdown to the current GUI instance. /// + /// /// The values of the current drodown. /// The names of those values. /// The default selected index. @@ -496,11 +499,13 @@ public static GuiComposer AddDropDown(this GuiComposer composer, string[] values /// /// Adds a dropdown to the current GUI instance. /// + /// /// The values of the current drodown. /// The names of those values. /// The default selected index. /// The event fired when the index is changed. /// The bounds of the index. + /// /// The name of this dropdown. public static GuiComposer AddDropDown(this GuiComposer composer, string[] values, string[] names, int selectedIndex, SelectionChangedDelegate onSelectionChanged, ElementBounds bounds, CairoFont font, string key = null) { @@ -512,10 +517,10 @@ public static GuiComposer AddDropDown(this GuiComposer composer, string[] values } - /// /// Gets the Drop Down element from the GUIComposer by their key. /// + /// /// the name of the dropdown to fetch. public static GuiElementDropDown GetDropDown(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementElementListPickerBase.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementElementListPickerBase.cs index 131fdf70..5003bcd1 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementElementListPickerBase.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementElementListPickerBase.cs @@ -37,8 +37,7 @@ public string TooltipText /// Constructor for the button /// /// The core client API. - /// - /// The action that happens when the button is toggled. + /// /// The bounding box of the button. public GuiElementElementListPickerBase(ICoreClientAPI capi, T elem, ElementBounds bounds) : base(capi, bounds) { @@ -170,11 +169,13 @@ public static partial class GuiComposerHelpers /// /// Adds multiple buttons with Text. /// - /// The texts on all the buttons. - /// The font for the buttons + /// /// The event fired when the button is pressed. - /// The bounds of the buttons. + /// The bounds of the buttons. + /// /// The key given to the bundle of buttons. + /// + /// public static GuiComposer AddElementListPicker(this GuiComposer composer, Type pickertype, T[] elems, Action onToggle, ElementBounds startBounds, int maxLineWidth, string key) { if (!composer.Composed) diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementHorizontalTabs.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementHorizontalTabs.cs index 255678be..818f4f33 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementHorizontalTabs.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementHorizontalTabs.cs @@ -42,6 +42,7 @@ public class GuiElementHorizontalTabs : GuiElementTextBase /// The client API /// A collection of GUI tabs. /// The font for the name of each tab. + /// /// The bounds of each tab. /// The event fired whenever the tab is clicked. public GuiElementHorizontalTabs(ICoreClientAPI capi, GuiTab[] tabs, CairoFont font, CairoFont selectedFont, ElementBounds bounds, Action onTabClicked) : base(capi, "", font, bounds) @@ -277,6 +278,7 @@ public override void OnMouseDownOnElement(ICoreClientAPI api, MouseEvent args) /// Sets the current tab to the given index. /// /// The current index of the tab. + /// public void SetValue(int selectedIndex, bool callhandler=true) { if (callhandler) @@ -303,20 +305,21 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a set of horizontal tabs to the GUI. /// + /// /// The collection of tabs. /// The bounds of the horizontal tabs. - /// The event fired when the tab is clicked. + /// The event fired when the tab is clicked. /// The font of the tabs. + /// /// The key for the added horizontal tabs. - public static GuiComposer AddHorizontalTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action OnTabClicked, CairoFont font, CairoFont selectedFont, string key = null) + public static GuiComposer AddHorizontalTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action onTabClicked, CairoFont font, CairoFont selectedFont, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementHorizontalTabs(composer.Api, tabs, font, selectedFont, bounds, OnTabClicked), key); + composer.AddInteractiveElement(new GuiElementHorizontalTabs(composer.Api, tabs, font, selectedFont, bounds, onTabClicked), key); } return composer; @@ -325,6 +328,7 @@ public static GuiComposer AddHorizontalTabs(this GuiComposer composer, GuiTab[] /// /// Gets the HorizontalTabs element from the GUI by name. /// + /// /// The key for the horizontal tabs you want to get. public static GuiElementHorizontalTabs GetHorizontalTabs(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementListMenu.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementListMenu.cs index 220a5495..53111c63 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementListMenu.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementListMenu.cs @@ -107,6 +107,8 @@ public override bool Focusable /// The default selected index. /// The event fired when the selection is changed. /// The bounds of the GUI element. + /// + /// public GuiElementListMenu(ICoreClientAPI capi, string[] values, string[] names, int selectedIndex, SelectionChangedDelegate onSelectionChanged, ElementBounds bounds, CairoFont font, bool multiSelect) : base(capi, "", font, bounds) { if (values.Length != names.Length) throw new ArgumentException("Values and Names arrays must be of the same length!"); diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementScrollbar.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementScrollbar.cs index 1290554a..845154d1 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementScrollbar.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementScrollbar.cs @@ -290,6 +290,7 @@ public static partial class GuiComposerHelpers /// /// Adds a vertical scrollbar to the GUI. /// + /// /// The action when the scrollbar changes. /// The bounds of the scrollbar. /// The name of the scrollbar. @@ -301,11 +302,12 @@ public static GuiComposer AddVerticalScrollbar(this GuiComposer composer, Action } return composer; } - + /// /// Gets the scrollbar by name. /// + /// /// The name of the scrollbar. /// The scrollbar itself. public static GuiElementScrollbar GetScrollbar(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSlider.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSlider.cs index 0a3b3529..a24262ef 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSlider.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSlider.cs @@ -392,10 +392,10 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a slider to the current GUI. /// + /// /// The event that fires when the slider's value is changed. /// The bounds of the slider. /// the internal name of the slider. @@ -411,6 +411,7 @@ public static GuiComposer AddSlider(this GuiComposer composer, ActionConsumable< /// /// Gets the slider by name from the GUI. /// + /// /// the internal name of the slider. /// the slider. public static GuiElementSlider GetSlider(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSwitch.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSwitch.cs index 8ccaab06..ee560459 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSwitch.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementSwitch.cs @@ -121,10 +121,10 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a switch to the GUI. /// + /// /// The event that happens when the switch is toggled. /// The bounds of the switch. /// the name of the switch. (Default: null) @@ -142,6 +142,7 @@ public static GuiComposer AddSwitch(this GuiComposer composer, Action onTo /// /// Gets the switch by name. /// + /// /// The internal name of the switch. /// Returns the named switch. public static GuiElementSwitch GetSwitch(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementTextButton.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementTextButton.cs index 80a72acd..a88af3c1 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementTextButton.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementTextButton.cs @@ -19,6 +19,7 @@ public class GuiElementTextButton : GuiElementControl GuiElementStaticText pressedText; LoadedTexture normalTexture; + LoadedTexture activeTexture; LoadedTexture hoverTexture; LoadedTexture disabledTexture; @@ -63,6 +64,7 @@ public string Text public GuiElementTextButton(ICoreClientAPI capi, string text, CairoFont font, CairoFont hoverFont, ActionConsumable onClick, ElementBounds bounds, EnumButtonStyle style = EnumButtonStyle.Normal) : base(capi, bounds) { hoverTexture = new LoadedTexture(capi); + activeTexture = new LoadedTexture(capi); normalTexture = new LoadedTexture(capi); disabledTexture = new LoadedTexture(capi); this.buttonStyle = style; @@ -108,7 +110,7 @@ public override void ComposeElements(Context ctxStatic, ImageSurface surfaceStat ctx.Clear(); - // 2. Hover button + // 2. Active button if (buttonStyle != EnumButtonStyle.None) { ctx.SetSourceRGBA(0, 0, 0, 0.4); @@ -120,13 +122,23 @@ public override void ComposeElements(Context ctxStatic, ImageSurface surfaceStat pressedText.ComposeElements(ctx, surface); pressedText.Bounds.fixedY -= textOffsetY; - generateTexture(surface, ref hoverTexture); + generateTexture(surface, ref activeTexture); + + // 3. Hover button + ctx.Clear(); + if (buttonStyle != EnumButtonStyle.None) + { + ctx.SetSourceRGBA(1, 1, 1, 0.1); + ctx.Rectangle(0, 0, Bounds.OuterWidth, Bounds.OuterHeight); + ctx.Fill(); + } + generateTexture(surface, ref hoverTexture); ctx.Dispose(); surface.Dispose(); - // 3. Disabled button + // 4. Disabled button surface = new ImageSurface(Format.Argb32, 2, 2); ctx = genContext(surface); @@ -231,7 +243,11 @@ public override void RenderInteractiveElements(float deltaTime) { api.Render.Render2DTexturePremultipliedAlpha(disabledTexture.TextureId, Bounds); } - else if (isOver || currentlyMouseDownOnElement) + else if (active || currentlyMouseDownOnElement) + { + api.Render.Render2DTexturePremultipliedAlpha(activeTexture.TextureId, Bounds); + } + else if (isOver) { api.Render.Render2DTexturePremultipliedAlpha(hoverTexture.TextureId, Bounds); } @@ -260,17 +276,14 @@ public override void OnKeyDown(ICoreClientAPI api, KeyEvent args) public override void OnMouseMove(ICoreClientAPI api, MouseEvent args) { - if (!Visible) return; - if ((enabled && Bounds.PointInside(api.Input.MouseX, api.Input.MouseY)) || active) - { - if (!isOver && PlaySound) api.Gui.PlaySound("menubutton"); - isOver = true; + bool wasOver = isOver; + setIsOver(); + if (!wasOver && isOver && PlaySound) api.Gui.PlaySound("menubutton"); + } - } - else - { - isOver = false; - } + protected void setIsOver() + { + isOver = Visible && enabled && Bounds.PointInside(api.Input.MouseX, api.Input.MouseY); } public override void OnMouseDownOnElement(ICoreClientAPI api, MouseEvent args) @@ -281,11 +294,15 @@ public override void OnMouseDownOnElement(ICoreClientAPI api, MouseEvent args) base.OnMouseDownOnElement(api, args); currentlyMouseDownOnElement = true; + + if (PlaySound) api.Gui.PlaySound("menubutton_down"); + setIsOver(); } public override void OnMouseUp(ICoreClientAPI api, MouseEvent args) { if (!Visible) return; + if (currentlyMouseDownOnElement && !Bounds.PointInside(args.X, args.Y) && !active && PlaySound) api.Gui.PlaySound("menubutton_up"); base.OnMouseUp(api, args); @@ -297,10 +314,6 @@ public override void OnMouseUpOnElement(ICoreClientAPI api, MouseEvent args) { if (enabled && currentlyMouseDownOnElement && Bounds.PointInside(args.X, args.Y) && args.Button == EnumMouseButton.Left) { - if (PlaySound) - { - api.Gui.PlaySound("menubutton_press"); - } args.Handled = onClick(); } @@ -322,8 +335,8 @@ public override void Dispose() { base.Dispose(); - hoverTexture.Dispose(); - normalText?.Dispose(); + hoverTexture?.Dispose(); + activeTexture?.Dispose(); pressedText?.Dispose(); disabledTexture?.Dispose(); normalTexture?.Dispose(); diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementToggleButton.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementToggleButton.cs index 8354eebd..f5faff06 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementToggleButton.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementToggleButton.cs @@ -259,6 +259,7 @@ public static partial class GuiComposerHelpers /// /// Gets the toggle button by name in the GUIComposer. /// + /// /// The name of the button. /// A button. public static GuiElementToggleButton GetToggleButton(this GuiComposer composer, string key) @@ -270,6 +271,7 @@ public static GuiElementToggleButton GetToggleButton(this GuiComposer composer, /// /// Creates a toggle button with the given parameters. /// + /// /// The text of the button. /// The font of the text. /// The event that happens once the button is toggled. @@ -287,6 +289,7 @@ public static GuiComposer AddToggleButton(this GuiComposer composer, string text /// /// Adds an icon button. /// + /// /// The name of the icon. /// The event that happens once the button is toggled. /// The bounding box of the button. @@ -303,6 +306,7 @@ public static GuiComposer AddIconButton(this GuiComposer composer, string icon, /// /// Toggles the given button. /// + /// /// The name of the button that was set. /// the index of the button. public static void ToggleButtonsSetValue(this GuiComposer composer, string key, int selectedIndex) @@ -319,6 +323,7 @@ public static void ToggleButtonsSetValue(this GuiComposer composer, string key, /// /// Adds multiple buttons with icons. /// + /// /// The collection of icons for the buttons. /// The font for the buttons. /// The event called when the buttons are pressed. @@ -360,6 +365,7 @@ public static GuiComposer AddIconToggleButtons(this GuiComposer composer, string /// /// Adds multiple buttons with Text. /// + /// /// The texts on all the buttons. /// The font for the buttons /// The event fired when the button is pressed. diff --git a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementVerticalTabs.cs b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementVerticalTabs.cs index b6e9cbd8..df07b8a0 100644 --- a/Client/UI/Elements/Impl/Interactive/Controls/GuiElementVerticalTabs.cs +++ b/Client/UI/Elements/Impl/Interactive/Controls/GuiElementVerticalTabs.cs @@ -34,6 +34,7 @@ public class GuiElementVerticalTabs : GuiElementTextBase /// The Client API /// The collection of individual tabs. /// The font for the group of them all. + /// /// The bounds of the tabs. /// The event fired when the tab is clicked. public GuiElementVerticalTabs(ICoreClientAPI capi, GuiTab[] tabs, CairoFont font, CairoFont selectedFont, ElementBounds bounds, Action onTabClicked) : base(capi, "", font, bounds) @@ -328,21 +329,21 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds multiple tabs to a group of vertical tabs. /// + /// /// The tabs being added. /// The boundaries of the tab group. - /// The event fired when any of the tabs are clicked. + /// The event fired when any of the tabs are clicked. /// The name of this tab group. - public static GuiComposer AddVerticalToggleTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action OnTabClicked, string key = null) + public static GuiComposer AddVerticalToggleTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action onTabClicked, string key = null) { if (!composer.Composed) { CairoFont font = CairoFont.WhiteDetailText().WithFontSize(17); CairoFont selectedFont = CairoFont.WhiteDetailText().WithFontSize(17).WithColor(GuiStyle.ActiveButtonTextColor); - var tabsElem = new GuiElementVerticalTabs(composer.Api, tabs, font, selectedFont, bounds, OnTabClicked); + var tabsElem = new GuiElementVerticalTabs(composer.Api, tabs, font, selectedFont, bounds, onTabClicked); tabsElem.ToggleTabs = true; composer.AddInteractiveElement(tabsElem, key); } @@ -353,17 +354,18 @@ public static GuiComposer AddVerticalToggleTabs(this GuiComposer composer, GuiTa /// /// Adds multiple tabs to a group of vertical tabs. /// + /// /// The tabs being added. /// The boundaries of the tab group. - /// The event fired when any of the tabs are clicked. + /// The event fired when any of the tabs are clicked. /// The name of this tab group. - public static GuiComposer AddVerticalTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action OnTabClicked, string key = null) + public static GuiComposer AddVerticalTabs(this GuiComposer composer, GuiTab[] tabs, ElementBounds bounds, Action onTabClicked, string key = null) { if (!composer.Composed) { CairoFont font = CairoFont.WhiteDetailText().WithFontSize(17); CairoFont selectedFont = CairoFont.WhiteDetailText().WithFontSize(17).WithColor(GuiStyle.ActiveButtonTextColor); - composer.AddInteractiveElement(new GuiElementVerticalTabs(composer.Api, tabs, font, selectedFont, bounds, OnTabClicked), key); + composer.AddInteractiveElement(new GuiElementVerticalTabs(composer.Api, tabs, font, selectedFont, bounds, onTabClicked), key); } return composer; @@ -372,6 +374,7 @@ public static GuiComposer AddVerticalTabs(this GuiComposer composer, GuiTab[] ta /// /// Gets the vertical tab group as declared by name. /// + /// /// The name of the vertical tab group to get. public static GuiElementVerticalTabs GetVerticalTab(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementCellList.cs b/Client/UI/Elements/Impl/Interactive/GuiElementCellList.cs index 818ded77..43d41ad2 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementCellList.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementCellList.cs @@ -312,6 +312,7 @@ public static partial class GuiComposerHelpers /// /// Adds a List to the current GUI. /// + /// /// The bounds of the cell. /// the event fired when the cell is requested by the GUI /// The cells of the list. @@ -329,6 +330,7 @@ public static GuiComposer AddCellList(this GuiComposer composer, ElementBound /// /// Gets the list by name. /// + /// /// The name of the list to get. /// public static GuiElementCellList GetCellList(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementChatInput.cs b/Client/UI/Elements/Impl/Interactive/GuiElementChatInput.cs index c84dd276..6fb0dfc7 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementChatInput.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementChatInput.cs @@ -92,14 +92,15 @@ public static partial class GuiComposerHelpers /// /// Adds a chat input to the GUI. /// + /// /// The bounds of the text. - /// The event fired when the text is changed. + /// The event fired when the text is changed. /// The name of this chat component. - public static GuiComposer AddChatInput(this GuiComposer composer, ElementBounds bounds, Action OnTextChanged, string key = null) + public static GuiComposer AddChatInput(this GuiComposer composer, ElementBounds bounds, Action onTextChanged, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementChatInput(composer.Api, bounds, OnTextChanged), key); + composer.AddInteractiveElement(new GuiElementChatInput(composer.Api, bounds, onTextChanged), key); } return composer; @@ -108,6 +109,7 @@ public static GuiComposer AddChatInput(this GuiComposer composer, ElementBounds /// /// Gets the chat input by name. /// + /// /// The name of the chat input component. /// The named component. public static GuiElementChatInput GetChatInput(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementConfigList.cs b/Client/UI/Elements/Impl/Interactive/GuiElementConfigList.cs index d83e736d..856a7844 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementConfigList.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementConfigList.cs @@ -263,16 +263,17 @@ public static partial class GuiComposerHelpers /// /// Adds a config List to the current GUI. /// + /// /// The items to add. - /// The event fired when the item is clicked. + /// The event fired when the item is clicked. /// The font of the Config List. /// The bounds of the config list. /// The name of the config list. - public static GuiComposer AddConfigList(this GuiComposer composer, List items, ConfigItemClickDelegate OnItemClick, CairoFont font, ElementBounds bounds, string key = null) + public static GuiComposer AddConfigList(this GuiComposer composer, List items, ConfigItemClickDelegate onItemClick, CairoFont font, ElementBounds bounds, string key = null) { if (!composer.Composed) { - GuiElementConfigList element = new GuiElementConfigList(composer.Api, items, OnItemClick, font, bounds); + GuiElementConfigList element = new GuiElementConfigList(composer.Api, items, onItemClick, font, bounds); composer.AddInteractiveElement(element, key); } @@ -282,6 +283,7 @@ public static GuiComposer AddConfigList(this GuiComposer composer, List /// Gets the config list by name. /// + /// /// The name of the config list. /// public static GuiElementConfigList GetConfigList(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementContainer.cs b/Client/UI/Elements/Impl/Interactive/GuiElementContainer.cs index 16a2c001..198ebb43 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementContainer.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementContainer.cs @@ -45,10 +45,6 @@ public class GuiElementContainer : GuiElement /// /// The Client API. /// The bounds of the list. - /// The function fired when the cell is clicked on the left side. - /// The function fired when the cell is clicked on the right side. - /// The event fired when a cell is requested by the gui - /// The array of cells initialized with the list. public GuiElementContainer(ICoreClientAPI capi, ElementBounds bounds) : base(capi, bounds) { listTexture = new LoadedTexture(capi); diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementDialogTitleBar.cs b/Client/UI/Elements/Impl/Interactive/GuiElementDialogTitleBar.cs index 97905e8f..1ef24477 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementDialogTitleBar.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementDialogTitleBar.cs @@ -382,15 +382,16 @@ public static partial class GuiComposerHelpers /// /// Adds a dialog title bar to the GUI. /// + /// /// The text of the title bar. - /// The event fired when the title bar is closed. + /// The event fired when the title bar is closed. /// The font of the title bar. /// The bounds of the title bar. - public static GuiComposer AddDialogTitleBar(this GuiComposer composer, string text, Action OnClose = null, CairoFont font = null, ElementBounds bounds = null) + public static GuiComposer AddDialogTitleBar(this GuiComposer composer, string text, Action onClose = null, CairoFont font = null, ElementBounds bounds = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementDialogTitleBar(composer.Api, text, composer, OnClose, font, bounds)); + composer.AddInteractiveElement(new GuiElementDialogTitleBar(composer.Api, text, composer, onClose, font, bounds)); } return composer; @@ -400,15 +401,16 @@ public static GuiComposer AddDialogTitleBar(this GuiComposer composer, string te /// /// Adds a dialog title bar to the GUI with a background. /// + /// /// The text of the title bar. - /// The event fired when the title bar is closed. + /// The event fired when the title bar is closed. /// The font of the title bar. /// The bounds of the title bar. - public static GuiComposer AddDialogTitleBarWithBg(this GuiComposer composer, string text, Action OnClose = null, CairoFont font = null, ElementBounds bounds = null) + public static GuiComposer AddDialogTitleBarWithBg(this GuiComposer composer, string text, Action onClose = null, CairoFont font = null, ElementBounds bounds = null) { if (!composer.Composed) { - GuiElementDialogTitleBar elem = new GuiElementDialogTitleBar(composer.Api, text, composer, OnClose, font, bounds); + GuiElementDialogTitleBar elem = new GuiElementDialogTitleBar(composer.Api, text, composer, onClose, font, bounds); elem.drawBg = true; composer.AddInteractiveElement(elem); } diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementNumberInput.cs b/Client/UI/Elements/Impl/Interactive/GuiElementNumberInput.cs index cae1e37a..c609374b 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementNumberInput.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementNumberInput.cs @@ -221,11 +221,12 @@ public static partial class GuiComposerHelpers /// /// Adds a numeric input for the current GUI. /// + /// /// The bounds of the number input. - /// The event fired when the number is changed. + /// The event fired when the number is changed. /// The font for the numbers. /// The name for this GuiElementNumberInput - public static GuiComposer AddNumberInput(this GuiComposer composer, ElementBounds bounds, Action OnTextChanged, CairoFont font = null, string key = null) + public static GuiComposer AddNumberInput(this GuiComposer composer, ElementBounds bounds, Action onTextChanged, CairoFont font = null, string key = null) { if (font == null) { @@ -234,7 +235,7 @@ public static GuiComposer AddNumberInput(this GuiComposer composer, ElementBound if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementNumberInput(composer.Api, bounds, OnTextChanged, font), key); + composer.AddInteractiveElement(new GuiElementNumberInput(composer.Api, bounds, onTextChanged, font), key); } return composer; @@ -243,6 +244,7 @@ public static GuiComposer AddNumberInput(this GuiComposer composer, ElementBound /// /// Gets the number input by name. /// + /// /// The internal name of the numeric input. /// The named numeric input. public static GuiElementNumberInput GetNumberInput(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementStatbar.cs b/Client/UI/Elements/Impl/Interactive/GuiElementStatbar.cs index e4f54b91..479d5d82 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementStatbar.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementStatbar.cs @@ -48,6 +48,7 @@ public class GuiElementStatbar : GuiElementTextBase /// The bounds of the stat bar. /// The color of the stat bar. /// Determines the direction that the bar fills. + /// public GuiElementStatbar(ICoreClientAPI capi, ElementBounds bounds, double[] color, bool rightToLeft, bool hideable) : base(capi, "", CairoFont.WhiteDetailText(), bounds) { barTexture = new LoadedTexture(capi); @@ -323,10 +324,10 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a stat bar to the current GUI with a minimum of 0 and a maximum of 100. /// + /// /// The bounds of the stat bar. /// The color of the stat bar. /// If true, the element can be fully hidden without recompose. @@ -352,6 +353,7 @@ public static GuiComposer AddStatbar(this GuiComposer composer, ElementBounds bo /// /// Adds a stat bar with filling in the opposite direction. Default values are from 0 to 100. /// + /// /// the bounds of the stat bar. /// the color of the stat bar. /// The internal name of the stat bar. @@ -367,6 +369,7 @@ public static GuiComposer AddInvStatbar(this GuiComposer composer, ElementBounds /// /// Gets the stat bar by name. /// + /// /// The internal name of the stat bar to fetch. /// The named stat bar. public static GuiElementStatbar GetStatbar(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementTextArea.cs b/Client/UI/Elements/Impl/Interactive/GuiElementTextArea.cs index 5993b93d..f0d6e5c0 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementTextArea.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementTextArea.cs @@ -98,11 +98,12 @@ public static partial class GuiComposerHelpers /// /// Adds a text area to the GUI. /// + /// /// The bounds of the Text Area - /// The event fired when the text is changed. + /// The event fired when the text is changed. /// The font of the text. /// The name of the text area. - public static GuiComposer AddTextArea(this GuiComposer composer, ElementBounds bounds, Action OnTextChanged, CairoFont font = null, string key = null) + public static GuiComposer AddTextArea(this GuiComposer composer, ElementBounds bounds, Action onTextChanged, CairoFont font = null, string key = null) { if (font == null) { @@ -111,7 +112,7 @@ public static GuiComposer AddTextArea(this GuiComposer composer, ElementBounds b if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementTextArea(composer.Api, bounds, OnTextChanged, font), key); + composer.AddInteractiveElement(new GuiElementTextArea(composer.Api, bounds, onTextChanged, font), key); } return composer; @@ -120,6 +121,7 @@ public static GuiComposer AddTextArea(this GuiComposer composer, ElementBounds b /// /// Gets the text area by name. /// + /// /// The name of the text area. /// The named Text Area. public static GuiElementTextArea GetTextArea(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/GuiElementTextInput.cs b/Client/UI/Elements/Impl/Interactive/GuiElementTextInput.cs index e0314669..63a83f07 100644 --- a/Client/UI/Elements/Impl/Interactive/GuiElementTextInput.cs +++ b/Client/UI/Elements/Impl/Interactive/GuiElementTextInput.cs @@ -20,12 +20,12 @@ public class GuiElementTextInput : GuiElementEditableTextBase /// /// The Client API /// The bounds of the text input. - /// The event fired when the text is changed. + /// The event fired when the text is changed. /// The font of the text. - public GuiElementTextInput(ICoreClientAPI capi, ElementBounds bounds, ActionOnTextChanged, CairoFont font) : base(capi, font, bounds) + public GuiElementTextInput(ICoreClientAPI capi, ElementBounds bounds, ActiononTextChanged, CairoFont font) : base(capi, font, bounds) { MouseOverCursor = "textselect"; - this.OnTextChanged = OnTextChanged; + this.OnTextChanged = onTextChanged; highlightTexture = new LoadedTexture(capi); } @@ -145,15 +145,15 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a text input to the current GUI. /// + /// /// The bounds of the text input. - /// The event fired when the text is changed. + /// The event fired when the text is changed. /// The font of the text. /// The name of this text component. - public static GuiComposer AddTextInput(this GuiComposer composer, ElementBounds bounds, Action OnTextChanged, CairoFont font = null, string key = null) + public static GuiComposer AddTextInput(this GuiComposer composer, ElementBounds bounds, Action onTextChanged, CairoFont font = null, string key = null) { if (font == null) { @@ -162,7 +162,7 @@ public static GuiComposer AddTextInput(this GuiComposer composer, ElementBounds if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementTextInput(composer.Api, bounds, OnTextChanged, font), key); + composer.AddInteractiveElement(new GuiElementTextInput(composer.Api, bounds, onTextChanged, font), key); } return composer; @@ -171,6 +171,7 @@ public static GuiComposer AddTextInput(this GuiComposer composer, ElementBounds /// /// Gets the text input by input name. /// + /// /// The name of the text input to get. /// The named text input public static GuiElementTextInput GetTextInput(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGrid.cs b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGrid.cs index 4d7a0c88..ca82176c 100644 --- a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGrid.cs +++ b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGrid.cs @@ -50,20 +50,20 @@ public void DetermineAvailableSlots(int[] visibleSlots = null) public static partial class GuiComposerHelpers { - /// /// Adds an item slot grid to the GUI. /// + /// /// The inventory attached to the slot grid. - /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced + /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced /// The number of columns in the slot grid. /// the bounds of the slot grid. /// The key for this particular slot grid. - public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory inventory, Action SendPacket, int columns, ElementBounds bounds, string key=null) + public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory inventory, Action sendPacket, int columns, ElementBounds bounds, string key=null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementItemSlotGrid(composer.Api, inventory, SendPacket, columns, null, bounds), key); + composer.AddInteractiveElement(new GuiElementItemSlotGrid(composer.Api, inventory, sendPacket, columns, null, bounds), key); GuiElementItemSlotGridBase.UpdateLastSlotGridFlag(composer); } return composer; @@ -72,17 +72,18 @@ public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory /// /// Adds an item slot grid to the GUI. /// + /// /// The inventory attached to the slot grid. - /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced + /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced /// The number of columns in the slot grid. /// The slots within the inventory that are currently accessible. /// the bounds of the slot grid. /// The key for this particular slot grid. - public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory inventory, Action SendPacket, int columns, int[] selectiveSlots, ElementBounds bounds, string key = null) + public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory inventory, Action sendPacket, int columns, int[] selectiveSlots, ElementBounds bounds, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementItemSlotGrid(composer.Api, inventory, SendPacket, columns, selectiveSlots, bounds), key); + composer.AddInteractiveElement(new GuiElementItemSlotGrid(composer.Api, inventory, sendPacket, columns, selectiveSlots, bounds), key); GuiElementItemSlotGridBase.UpdateLastSlotGridFlag(composer); } @@ -92,6 +93,7 @@ public static GuiComposer AddItemSlotGrid(this GuiComposer composer, IInventory /// /// Gets the slot grid by name. /// + /// /// The name of the slot grid to get. public static GuiElementItemSlotGrid GetSlotGrid(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridBase.cs b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridBase.cs index 8f0c7c02..849ac2c2 100644 --- a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridBase.cs +++ b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridBase.cs @@ -80,10 +80,10 @@ public override bool Focusable /// /// The client API /// The attached inventory - /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced + /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced /// The number of columns in the GUI. /// The bounds of the slot grid. - public GuiElementItemSlotGridBase(ICoreClientAPI capi, IInventory inventory, Action SendPacket, int columns, ElementBounds bounds) : base(capi, bounds) + public GuiElementItemSlotGridBase(ICoreClientAPI capi, IInventory inventory, Action sendPacket, int columns, ElementBounds bounds) : base(capi, bounds) { slotTexture = new LoadedTexture(capi); highlightSlotTexture = new LoadedTexture(capi); @@ -92,7 +92,7 @@ public GuiElementItemSlotGridBase(ICoreClientAPI capi, IInventory inventory, Act prevSlotQuantity = inventory.Count; this.inventory = inventory; cols = columns; - SendPacketHandler = SendPacket; + SendPacketHandler = sendPacket; inventory.SlotNotified += OnSlotNotified; @@ -527,13 +527,13 @@ public override int OutlineColor() return (255 << 8) + (255 << 24); } - /// /// Renders only a subset of all available slots filtered by searching given text on the item name/description /// /// /// Can be set to increase search performance, otherwise a slow search is performed + /// public void FilterItemsBySearchText(string text, Dictionary searchCache = null, Dictionary searchCacheNames = null) { searchText = text.RemoveDiacritics().ToLowerInvariant(); diff --git a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridExcl.cs b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridExcl.cs index 4db2abca..5efaa085 100644 --- a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridExcl.cs +++ b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementItemSlotGridExcl.cs @@ -17,15 +17,15 @@ public class GuiElementItemSlotGridExcl : GuiElementItemSlotGridBase /// /// The Client API /// The attached inventory. - /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced + /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced /// The number of columns in the slot grid. /// The slots that have been excluded. /// The bounds of the slot grid. - public GuiElementItemSlotGridExcl(ICoreClientAPI capi, IInventory inventory, Action SendPacketHandler, int columns, int[] excludingSlots, ElementBounds bounds) : base(capi, inventory, SendPacketHandler, columns, bounds) + public GuiElementItemSlotGridExcl(ICoreClientAPI capi, IInventory inventory, Action sendPacketHandler, int columns, int[] excludingSlots, ElementBounds bounds) : base(capi, inventory, sendPacketHandler, columns, bounds) { this.excludingSlots = excludingSlots; InitDicts(); - this.SendPacketHandler = SendPacketHandler; + this.SendPacketHandler = sendPacketHandler; } @@ -77,21 +77,21 @@ public override void PostRenderInteractiveElements(float deltaTime) public static partial class GuiComposerHelpers { - /// /// Adds an ItemSlotGrid with Exclusions. /// + /// /// The attached inventory. - /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced + /// A handler that should send supplied network packet to the server, if the inventory modifications should be synced /// The number of columns. /// The slots that have been excluded from the slot grid. /// The bounds of the slot grid. /// The name of the slot grid. - public static GuiComposer AddItemSlotGridExcl(this GuiComposer composer, IInventory inventory, Action SendPacket, int columns, int[] excludingSlots, ElementBounds bounds, string key = null) + public static GuiComposer AddItemSlotGridExcl(this GuiComposer composer, IInventory inventory, Action sendPacket, int columns, int[] excludingSlots, ElementBounds bounds, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementItemSlotGridExcl(composer.Api, inventory, SendPacket, columns, excludingSlots, bounds), key); + composer.AddInteractiveElement(new GuiElementItemSlotGridExcl(composer.Api, inventory, sendPacket, columns, excludingSlots, bounds), key); GuiElementItemSlotGridBase.UpdateLastSlotGridFlag(composer); } return composer; @@ -100,6 +100,7 @@ public static GuiComposer AddItemSlotGridExcl(this GuiComposer composer, IInvent /// /// Gets the ItemSlotGridExcl by name. /// + /// /// The name of the ItemSlotGridExcl public static GuiElementItemSlotGridExcl GetSlotGridExcl(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementPassiveItemSlot.cs b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementPassiveItemSlot.cs index ba674b95..70c695b1 100644 --- a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementPassiveItemSlot.cs +++ b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementPassiveItemSlot.cs @@ -80,10 +80,10 @@ public override void RenderInteractiveElements(float deltaTime) public static partial class GuiComposerHelpers { - /// /// Adds a passive item slot to the GUI. /// + /// /// The bounds of the Slot /// The inventory attached to the slot. /// The internal slot of the slot. diff --git a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementSkillItemGrid.cs b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementSkillItemGrid.cs index 8e5abe43..d41c10e6 100644 --- a/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementSkillItemGrid.cs +++ b/Client/UI/Elements/Impl/Interactive/Inventory/GuiElementSkillItemGrid.cs @@ -177,21 +177,21 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds a skill item grid to the GUI. /// + /// /// The items that represent skills. /// the columns in the skill item grid. /// The rows in the skill item grid. - /// The effect when a slot is clicked. + /// The effect when a slot is clicked. /// The bounds of the item grid. /// The name of the item grid to add. - public static GuiComposer AddSkillItemGrid(this GuiComposer composer, List skillItems, int columns, int rows, Action OnSlotClick, ElementBounds bounds, string key = null) + public static GuiComposer AddSkillItemGrid(this GuiComposer composer, List skillItems, int columns, int rows, Action onSlotClick, ElementBounds bounds, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementSkillItemGrid(composer.Api, skillItems, columns, rows, OnSlotClick, bounds), key); + composer.AddInteractiveElement(new GuiElementSkillItemGrid(composer.Api, skillItems, columns, rows, onSlotClick, bounds), key); } return composer; } @@ -199,6 +199,7 @@ public static GuiComposer AddSkillItemGrid(this GuiComposer composer, List /// Fetches the skill item grid by name /// + /// /// The name of the skill item grid to get. /// The skill item grid to get. public static GuiElementSkillItemGrid GetSkillItemGrid(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementDynamicText.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementDynamicText.cs index 1f8d7974..152c0531 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementDynamicText.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementDynamicText.cs @@ -30,7 +30,6 @@ public int QuantityTextLines { get /// The client API. /// The starting text on the component. /// The font of the text. - /// The orientation of the text. /// the bounds of the text. public GuiElementDynamicText(ICoreClientAPI capi, string text, CairoFont font, ElementBounds bounds) : base(capi, text, font, bounds) { @@ -112,6 +111,7 @@ public void SetNewTextAsync(string text, bool autoHeight = false, bool forceRedr /// The text of the component. /// Whether the height of the component should be modified. /// Whether the element should be redrawn. + /// public void SetNewText(string text, bool autoHeight = false, bool forceRedraw = false, bool async = false) { if (this.text != text || forceRedraw) @@ -141,7 +141,6 @@ public static class GuiElementDynamicTextHelper /// /// /// - /// /// /// /// @@ -171,6 +170,7 @@ public static GuiComposer AddDynamicText(this GuiComposer composer, string text, /// /// Gets the Dynamic Text by name from the GUI. /// + /// /// The name of the element. public static GuiElementDynamicText GetDynamicText(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementEditableTextBase.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementEditableTextBase.cs index 0f76bbbb..9a356356 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementEditableTextBase.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementEditableTextBase.cs @@ -258,6 +258,7 @@ public void SetValue(float value) /// Sets given text, sets the cursor to the end of the text /// /// + /// public void SetValue(string text, bool setCaretPosToEnd = true) { LoadValue(Lineize(text)); @@ -273,7 +274,7 @@ public void SetValue(string text, bool setCaretPosToEnd = true) /// /// Sets given texts, leaves cursor position unchanged /// - /// + /// public void LoadValue(List newLines) { // Disallow edit if prevent by event or if it adds another line beyond max lines diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementEmbossedText.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementEmbossedText.cs index f261de1e..6ae1f9fd 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementEmbossedText.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementEmbossedText.cs @@ -122,10 +122,10 @@ public override void Dispose() public static partial class GuiComposerHelpers { - /// /// Adds an embossed text component to the GUI. /// + /// /// The text of the component. /// The font of the text. /// The bounds of the component. @@ -142,6 +142,7 @@ public static GuiComposer AddEmbossedText(this GuiComposer composer, string text /// /// Gets the EmbossedText component by name. /// + /// /// The name of the component. /// the named component of the text. public static GuiElementEmbossedText GetEmbossedText(this GuiComposer composer, string key) diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementHoverText.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementHoverText.cs index 5dc3b4f4..816aeb98 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementHoverText.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementHoverText.cs @@ -70,6 +70,7 @@ public override double DrawOrder /// The font of the text. /// The width of the text. /// the bounds of the text. + /// public GuiElementHoverText(ICoreClientAPI capi, string text, CairoFont font, int maxWidth, ElementBounds bounds, TextBackground background = null) : base(capi, text, font, bounds) { this.Background = background; @@ -300,6 +301,7 @@ public static partial class GuiComposerHelpers /// /// Adds a hover text to the GUI. /// + /// /// The text of the text. /// The font of the text. /// The width of the text. @@ -331,6 +333,7 @@ public static GuiComposer AddAutoSizeHoverText(this GuiComposer composer, string /// /// Adds a hover text to the GUI. /// + /// /// The text of the text. /// The font of the text. /// The width of the text. @@ -350,10 +353,12 @@ public static GuiComposer AddTranspHoverText(this GuiComposer composer, string t /// /// Adds a hover text to the GUI. /// + /// /// The text of the text. /// The font of the text. /// The width of the text. /// The bounds of the text. + /// /// The name of this hover text component. public static GuiComposer AddHoverText(this GuiComposer composer, string text, CairoFont font, int width, ElementBounds bounds, TextBackground background, string key = null) { @@ -368,6 +373,7 @@ public static GuiComposer AddHoverText(this GuiComposer composer, string text, C /// /// Fetches the hover text component by name. /// + /// /// The name of the text component. public static GuiElementHoverText GetHoverText(this GuiComposer composer, string key) { diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementItemstackInfo.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementItemstackInfo.cs index b1ab8297..08774adc 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementItemstackInfo.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementItemstackInfo.cs @@ -245,6 +245,7 @@ public ItemSlot GetSlot() /// Sets the source slot for stacks. /// /// + /// /// True if recomposed public bool SetSourceSlot(ItemSlot nowSlot, bool forceRecompose = false) { diff --git a/Client/UI/Elements/Impl/Interactive/Text/GuiElementRichtext.cs b/Client/UI/Elements/Impl/Interactive/Text/GuiElementRichtext.cs index d45c8885..c90e18ad 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/GuiElementRichtext.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/GuiElementRichtext.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Cairo; using Vintagestory.API.Common; using Vintagestory.API.Config; diff --git a/Client/UI/Elements/Impl/Interactive/Text/Richtext/RichTextComponent.cs b/Client/UI/Elements/Impl/Interactive/Text/Richtext/RichTextComponent.cs index d84349de..4f53c3b5 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/Richtext/RichTextComponent.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/Richtext/RichTextComponent.cs @@ -1,6 +1,5 @@ using Cairo; using Vintagestory.API.Config; -using Vintagestory.API.MathTools; namespace Vintagestory.API.Client { @@ -74,7 +73,6 @@ double spaceWidth /// /// Context of the text component. /// The surface of the image. - /// The font for the element. public override void ComposeElements(Context ctx, ImageSurface surface) { textUtil.DrawMultilineText(ctx, Font, Lines, Font.Orientation); @@ -90,13 +88,13 @@ public override void ComposeElements(Context ctx, ImageSurface surface) } - /// /// Renders the text component. /// /// /// /// + /// public override void RenderInteractiveElements(float deltaTime, double renderX, double renderY, double renderZ) { /*for (int i = 0; i < Lines.Length; i++) @@ -111,7 +109,10 @@ public override void RenderInteractiveElements(float deltaTime, double renderX, /// Initializes the size and stuff. Return true if you had to enter the next line /// /// - /// + /// + /// + /// + /// /// True when longer than 1 line public override EnumCalcBoundsResult CalcBounds(TextFlowPath[] flowPath, double currentLineHeight, double offsetX, double lineY, out double nextOffsetX) { diff --git a/Client/UI/Elements/Impl/Interactive/Text/Richtext/SlideshowItemstackTextComponent.cs b/Client/UI/Elements/Impl/Interactive/Text/Richtext/SlideshowItemstackTextComponent.cs index 00ea5b49..e1c51464 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/Richtext/SlideshowItemstackTextComponent.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/Richtext/SlideshowItemstackTextComponent.cs @@ -42,9 +42,11 @@ public class SlideshowItemstackTextComponent : ItemstackComponentBase /// /// Flips through given array of item stacks every second /// + /// /// /// /// + /// public SlideshowItemstackTextComponent(ICoreClientAPI capi, ItemStack[] itemstacks, double unscaledSize, EnumFloat floatType, Action onStackClicked = null) : base(capi) { initSlot(); @@ -59,9 +61,12 @@ public SlideshowItemstackTextComponent(ICoreClientAPI capi, ItemStack[] itemstac /// /// Looks at the collectibles handbook groupBy attribute and makes a list of itemstacks from that /// + /// /// + /// /// /// + /// public SlideshowItemstackTextComponent(ICoreClientAPI capi, ItemStack itemstackgroup, List allstacks, double unscaleSize, EnumFloat floatType, Action onStackClicked = null) : base(capi) { initSlot(); diff --git a/Client/UI/Elements/Impl/Interactive/Text/Richtext/TextLinkComponent.cs b/Client/UI/Elements/Impl/Interactive/Text/Richtext/TextLinkComponent.cs index bb7d0ef2..d8f458e6 100644 --- a/Client/UI/Elements/Impl/Interactive/Text/Richtext/TextLinkComponent.cs +++ b/Client/UI/Elements/Impl/Interactive/Text/Richtext/TextLinkComponent.cs @@ -27,8 +27,10 @@ public bool Clickable { /// /// A text component with an embedded link. /// + /// /// The text of the Text. - /// The link in the text. + /// + /// public LinkTextComponent(ICoreClientAPI api, string displayText, CairoFont font, Action onLinkClicked) : base(api, displayText, font) { this.onLinkClicked = onLinkClicked; diff --git a/Client/UI/Elements/Impl/Misc/GuiElementClip.cs b/Client/UI/Elements/Impl/Misc/GuiElementClip.cs index ace71161..b05f9924 100644 --- a/Client/UI/Elements/Impl/Misc/GuiElementClip.cs +++ b/Client/UI/Elements/Impl/Misc/GuiElementClip.cs @@ -53,6 +53,7 @@ public static class GuiElementClipHelpler /// /// Add a clip area. Thhis select an area to be rendered, where anything outside will be invisible. Useful for scrollable content. Can be called multiple times, to reduce the render area further, but needs an equal amount of calls to EndClip() /// + /// /// The bounds of the object. public static GuiComposer BeginClip(this GuiComposer composer, ElementBounds bounds) { diff --git a/Client/UI/Elements/Impl/Misc/GuiElementCustomRender.cs b/Client/UI/Elements/Impl/Misc/GuiElementCustomRender.cs index 51a9c360..04e4e9ef 100644 --- a/Client/UI/Elements/Impl/Misc/GuiElementCustomRender.cs +++ b/Client/UI/Elements/Impl/Misc/GuiElementCustomRender.cs @@ -41,8 +41,9 @@ public static partial class GuiComposerHelpers /// /// Adds a static custom draw component to the GUI. /// + /// /// The bounds of the component. - /// The event fired when the element is drawn. + /// The event fired when the element is drawn. public static GuiComposer AddCustomRender(this GuiComposer composer, ElementBounds bounds, RenderDelegateWithBounds onRender) { if (!composer.Composed) diff --git a/Client/UI/Elements/Impl/Static/GuiElementCustomDraw.cs b/Client/UI/Elements/Impl/Static/GuiElementCustomDraw.cs index 9fbd97f2..09d1f2c3 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementCustomDraw.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementCustomDraw.cs @@ -69,13 +69,14 @@ public static partial class GuiComposerHelpers /// /// Adds a static custom draw component to the GUI. /// + /// /// The bounds of the component. - /// The event fired when the element is drawn. - public static GuiComposer AddStaticCustomDraw(this GuiComposer composer, ElementBounds bounds, DrawDelegateWithBounds OnDraw) + /// The event fired when the element is drawn. + public static GuiComposer AddStaticCustomDraw(this GuiComposer composer, ElementBounds bounds, DrawDelegateWithBounds onDraw) { if (!composer.Composed) { - composer.AddStaticElement(new GuiElementCustomDraw(composer.Api, bounds, OnDraw)); + composer.AddStaticElement(new GuiElementCustomDraw(composer.Api, bounds, onDraw)); } return composer; } @@ -83,14 +84,15 @@ public static GuiComposer AddStaticCustomDraw(this GuiComposer composer, Element /// /// Adds a dynamic custom draw component to the GUI. /// + /// /// The bounds of the component. - /// The event fired when the element is drawn. + /// The event fired when the element is drawn. /// The name of the element. - public static GuiComposer AddDynamicCustomDraw(this GuiComposer composer, ElementBounds bounds, DrawDelegateWithBounds OnDraw, string key = null) + public static GuiComposer AddDynamicCustomDraw(this GuiComposer composer, ElementBounds bounds, DrawDelegateWithBounds onDraw, string key = null) { if (!composer.Composed) { - composer.AddInteractiveElement(new GuiElementCustomDraw(composer.Api, bounds, OnDraw, true), key); + composer.AddInteractiveElement(new GuiElementCustomDraw(composer.Api, bounds, onDraw, true), key); } return composer; } diff --git a/Client/UI/Elements/Impl/Static/GuiElementDialogBackground.cs b/Client/UI/Elements/Impl/Static/GuiElementDialogBackground.cs index 40b82759..f029825d 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementDialogBackground.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementDialogBackground.cs @@ -18,8 +18,8 @@ public class GuiElementDialogBackground : GuiElement /// The Client API /// The bounds of the element. /// Minor style adjustments to accomodate title bar - /// Whether or not the hotbar is rendered in this gui. /// The top padding area of the GUI + /// public GuiElementDialogBackground(ICoreClientAPI capi, ElementBounds bounds, bool withTitlebar, double strokeWidth = 0, float alpha = 1) : base(capi, bounds) { this.strokeWidth = strokeWidth; diff --git a/Client/UI/Elements/Impl/Static/GuiElementGameOverlay.cs b/Client/UI/Elements/Impl/Static/GuiElementGameOverlay.cs index acfcdc45..084a710c 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementGameOverlay.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementGameOverlay.cs @@ -37,6 +37,7 @@ public static class GuiElementGameOverlyHelper /// /// Adds an overlay to the current GUI. /// + /// /// The bounds of the overlay. /// The background color of the overlay. public static GuiComposer AddGameOverlay(this GuiComposer composer, ElementBounds bounds, double[] backgroundColor = null) diff --git a/Client/UI/Elements/Impl/Static/GuiElementGrayBackground.cs b/Client/UI/Elements/Impl/Static/GuiElementGrayBackground.cs index ba757e5c..f729b150 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementGrayBackground.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementGrayBackground.cs @@ -31,6 +31,7 @@ public static class GuiElementGrayBackgroundHelpber /// /// Adds a gray background to the current GUI. /// + /// /// The bounds of the backgrounds. public static GuiComposer AddGrayBG(this GuiComposer composer, ElementBounds bounds) { diff --git a/Client/UI/Elements/Impl/Static/GuiElementImageBackground.cs b/Client/UI/Elements/Impl/Static/GuiElementImageBackground.cs index cd4676a8..ce4e97a0 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementImageBackground.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementImageBackground.cs @@ -17,6 +17,8 @@ class GuiElementImageBackground : GuiElement /// The bounds of the element. /// The name of the texture. /// The brightness of the texture. (Default: 1f) + /// + /// public GuiElementImageBackground(ICoreClientAPI capi, ElementBounds bounds, AssetLocation textureLoc, float brightness = 1f, float alpha = 1, float scale = 1f) : base(capi, bounds) { this.alpha = alpha; @@ -50,9 +52,12 @@ public static class GuiElementImageBackgroundHelper /// /// Adds a background to the current GUI /// + /// /// The bounds of the background /// The name of the background texture. /// The brightness of the texture (default: 1f) + /// + /// public static GuiComposer AddImageBG(this GuiComposer composer, ElementBounds bounds, AssetLocation textureLoc, float brightness = 1f, float alpha = 1, float scale = 1f) { if (!composer.Composed) diff --git a/Client/UI/Elements/Impl/Static/GuiElementInset.cs b/Client/UI/Elements/Impl/Static/GuiElementInset.cs index 8e560f26..a1c7a5d6 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementInset.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementInset.cs @@ -42,6 +42,7 @@ public static class GuiElementInsetHelper /// /// Adds an inset to the current GUI. /// + /// /// The bounds of the inset. /// The depth of the inset. /// The brightness of the inset. diff --git a/Client/UI/Elements/Impl/Static/GuiElementStaticText.cs b/Client/UI/Elements/Impl/Static/GuiElementStaticText.cs index 85a71550..71bf315b 100644 --- a/Client/UI/Elements/Impl/Static/GuiElementStaticText.cs +++ b/Client/UI/Elements/Impl/Static/GuiElementStaticText.cs @@ -68,6 +68,7 @@ public static partial class GuiComposerHelpers /// /// Adds a static text component to the GUI /// + /// /// The text of the text component. /// The font of the text. /// The bounds of the text container. @@ -84,6 +85,7 @@ public static GuiComposer AddStaticText(this GuiComposer composer, string text, /// /// Adds a static text component to the GUI /// + /// /// The text of the text component. /// The font of the text. /// The orientation of the text. @@ -101,6 +103,7 @@ public static GuiComposer AddStaticText(this GuiComposer composer, string text, /// /// Adds a static text component to the GUI that automatically resizes as necessary. /// + /// /// The text of the text component. /// The font of the text. /// The orientation of the text. @@ -121,9 +124,9 @@ public static GuiComposer AddStaticTextAutoBoxSize(this GuiComposer composer, st /// /// Adds a static text component to the GUI that automatically resizes as necessary. /// + /// /// The text of the text component. /// The font of the text. - /// The orientation of the text. /// The bounds of the text container. /// The name of the component. public static GuiComposer AddStaticTextAutoFontSize(this GuiComposer composer, string text, CairoFont font, ElementBounds bounds, string key = null) @@ -140,6 +143,7 @@ public static GuiComposer AddStaticTextAutoFontSize(this GuiComposer composer, s /// /// Gets the static text component by name. /// + /// /// The name of the component. public static GuiElementStaticText GetStaticText(this GuiComposer composer, string key) { diff --git a/Client/UI/GuiComposer.cs b/Client/UI/GuiComposer.cs index 1c3a996e..ba42ad50 100644 --- a/Client/UI/GuiComposer.cs +++ b/Client/UI/GuiComposer.cs @@ -613,7 +613,6 @@ public void OnKeyDown(KeyEvent args, bool haveFocus) /// Fires the OnKeyDown events. /// /// The keyboard information. - /// Whether or not the gui has focus. public void OnKeyUp(KeyEvent args) { if (!Enabled) return; diff --git a/Client/UI/TextDrawUtil.cs b/Client/UI/TextDrawUtil.cs index d4d76e06..7f7d526d 100644 --- a/Client/UI/TextDrawUtil.cs +++ b/Client/UI/TextDrawUtil.cs @@ -188,6 +188,7 @@ public double GetLineHeight(CairoFont font) /// /// The font of the text. /// The text itself. + /// /// The path for the text. /// The height of the line /// The number of lines. @@ -212,6 +213,7 @@ public int GetQuantityTextLines(CairoFont font, string text, EnumLinebreakBehavi /// /// The font of the text. /// The text itself. + /// /// The path for the text. /// The height of the line /// The final height of the text. @@ -226,9 +228,11 @@ public double GetMultilineTextHeight(CairoFont font, string text, EnumLinebreakB /// /// The font of the text. /// The text of the lines. + /// /// The flow direction of text. /// The offset start position for X /// The offset start position for Y + /// /// The text broken up into lines. public TextLine[] Lineize(CairoFont font, string fulltext, EnumLinebreakBehavior linebreak, TextFlowPath[] flowPath, double startOffsetX = 0, double startY = 0, bool keepLinebreakChar = false) { @@ -247,15 +251,17 @@ public TextLine[] Lineize(CairoFont font, string fulltext, EnumLinebreakBehavior } - /// /// Turns the supplied text into line of text constrained by supplied flow path and starting at supplied start coordinates /// /// Contexts of the GUI. /// The text to be split + /// /// Sets the general flow of text. /// The offset start position for X /// The offset start position for Y + /// + /// /// The text broken up into lines. public TextLine[] Lineize(Context ctx, string text, EnumLinebreakBehavior linebreak, TextFlowPath[] flowPath, double startOffsetX = 0, double startY = 0, double lineHeightMultiplier = 1f, bool keepLinebreakChar = false) { diff --git a/Client/UI/TextTextureUtil.cs b/Client/UI/TextTextureUtil.cs index 5cb8b768..9e399e35 100644 --- a/Client/UI/TextTextureUtil.cs +++ b/Client/UI/TextTextureUtil.cs @@ -26,6 +26,7 @@ public TextTextureUtil(ICoreClientAPI capi) /// The height of the text. /// The background of the text. (default: none/null) /// The orientation of the text. (default: left) + /// /// The texturized text. public LoadedTexture GenTextTexture(string text, CairoFont font, int width, int height, TextBackground background = null, EnumTextOrientation orientation = EnumTextOrientation.Left, bool demulAlpha = false) { @@ -44,6 +45,7 @@ public LoadedTexture GenTextTexture(string text, CairoFont font, int width, int /// The texture to be loaded on to. /// The background of the text. (default: none/null) /// The orientation of the text. (default: left) + /// public void GenOrUpdateTextTexture(string text, CairoFont font, int width, int height, ref LoadedTexture loadedTexture, TextBackground background = null, EnumTextOrientation orientation = EnumTextOrientation.Left, bool demulAlpha = false) { if (background == null) background = defaultBackground; diff --git a/Common/API/ChatCommand/CmdArgs.cs b/Common/API/ChatCommand/CmdArgs.cs index 0192128f..30b5bbd8 100644 --- a/Common/API/ChatCommand/CmdArgs.cs +++ b/Common/API/ChatCommand/CmdArgs.cs @@ -281,6 +281,7 @@ public void AppendSingle(string arg) ///
'true', 'yes' and '1' will all be interpreted as true. Parameter trueAlias (with default value 'on') allows one additional word to be used to signify true. Anything else will return false. /// /// + /// /// public bool? PopBool(bool? defaultValue = null, string trueAlias = "on") { diff --git a/Common/API/ChatCommand/IChatCommandApi.cs b/Common/API/ChatCommand/IChatCommandApi.cs index fc2ae088..33907571 100644 --- a/Common/API/ChatCommand/IChatCommandApi.cs +++ b/Common/API/ChatCommand/IChatCommandApi.cs @@ -181,11 +181,6 @@ public class TextCommandResult public static TextCommandResult Success(string message = "", object data = null) => new TextCommandResult() { Status = EnumCommandStatus.Success, Data = data, StatusMessage = message }; public static TextCommandResult Error(string message, string errorCode = "") => new TextCommandResult() { Status = EnumCommandStatus.Error, StatusMessage = message, ErrorCode = errorCode }; - internal static TextCommandResult Success(object fullSyntax) - { - throw new NotImplementedException(); - } - public static TextCommandResult Deferred => new TextCommandResult() { Status = EnumCommandStatus.Deferred }; public static OnCommandDelegate DeferredHandler => (args) => Deferred; diff --git a/Common/API/IBlockAccessor.cs b/Common/API/IBlockAccessor.cs index 49414396..aa31e230 100644 --- a/Common/API/IBlockAccessor.cs +++ b/Common/API/IBlockAccessor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Vintagestory.API.Common.Entities; using Vintagestory.API.MathTools; namespace Vintagestory.API.Common @@ -13,6 +14,10 @@ public class BlockUpdate /// public int OldBlockId; /// + /// Contains liquid layer of block + /// + public int OldFluidBlockId; + /// /// If this value is negative, it indicates no change to the block (neither air block nor anything else) because only the fluid is being updated /// public int NewSolidBlockId = -1; @@ -24,53 +29,32 @@ public class BlockUpdate public byte[] OldBlockEntityData; public byte[] NewBlockEntityData; - } - - public struct DecorUpdateKey : IEquatable - { - public int X; - public int Y; - public int Z; - public int DecorIndex; - - public bool Equals(DecorUpdateKey other) - { - return other.X == X && other.Y == Y && other.Z == Z && other.DecorIndex == DecorIndex; - } - - public override bool Equals(object obj) - { - return obj is DecorUpdateKey key && Equals(key); - } - - public override int GetHashCode() - { - int hashCode = -559056482; - hashCode = hashCode * -1521134295 + X.GetHashCode(); - hashCode = hashCode * -1521134295 + Y.GetHashCode(); - hashCode = hashCode * -1521134295 + Z.GetHashCode(); - hashCode = hashCode * -1521134295 + DecorIndex.GetHashCode(); - return hashCode; - } + public DecorUpdate[] Decor; } public class DecorUpdate { - public BlockPos Pos; public int OldDecorId; public int NewDecorId; - public int OldDecorIndex; - public int NewDecorIndex; } - public class BlockUpdateStruct + public class EntityUpdate { - public BlockPos Pos; - public int OldBlockId; - public int NewBlockId; - public ItemStack ByStack; - - public byte[] BlockEntityData; + /// + /// If set this entity was spawned or Moved (position needs to be set too) + /// + public long EntityId = -1; + + /// + /// If set this entity needs to be spawned + /// + public EntityProperties EntityProperties; + + /// + /// If set the entity was moved + /// + public EntityPos OldPosition; + public EntityPos NewPosition; } /// A climate condition at a given position diff --git a/Common/API/IBlockAccessorRevertable.cs b/Common/API/IBlockAccessorRevertable.cs index ef1fa5f9..81b2ec72 100644 --- a/Common/API/IBlockAccessorRevertable.cs +++ b/Common/API/IBlockAccessorRevertable.cs @@ -1,21 +1,21 @@ using System; +using System.Collections.Generic; +using Vintagestory.API.Common.Entities; using Vintagestory.API.MathTools; -using Vintagestory.API.Server; namespace Vintagestory.API.Common { public class HistoryState { - public static HistoryState Empty = new HistoryState() { BlockUpdates = new BlockUpdate[0], DecorUpdates = new DecorUpdate[0] }; + public static HistoryState Empty() => new() { BlockUpdates = Array.Empty() }; public BlockUpdate[] BlockUpdates; - public DecorUpdate[] DecorUpdates; - public object Data; public BlockPos OldStartMarker; public BlockPos OldEndMarker; public BlockPos NewStartMarker; public BlockPos NewEndMarker; + public List EntityUpdates; } @@ -35,7 +35,7 @@ public interface IBlockAccessorRevertable : IBulkBlockAccessor /// /// 0 = working on latest version, 1 = undo used one time, 2 = undo used 2 times, etc. /// - int CurrentyHistoryState { get; } + int CurrentHistoryState { get; } /// /// 1 = perform 1 undo @@ -66,9 +66,13 @@ public interface IBlockAccessorRevertable : IBulkBlockAccessor void CommitBlockEntityData(); void BeginMultiEdit(); + void EndMultiEdit(); void StoreHistoryState(HistoryState state); + void StoreEntitySpawnToHistory(Entity entity); + + void StoreEntityMoveToHistory(BlockPos start, BlockPos end, Vec3i offset); } } diff --git a/Common/API/IBulkBlockAccessor.cs b/Common/API/IBulkBlockAccessor.cs index 4cb88d01..fc29e441 100644 --- a/Common/API/IBulkBlockAccessor.cs +++ b/Common/API/IBulkBlockAccessor.cs @@ -17,8 +17,6 @@ public interface IBulkBlockAccessor : IBlockAccessor /// Dictionary StagedBlocks { get; } - Dictionary StagedDecors { get; } - /// /// If set to true, the methods GetBlock() and GetBlockId() will behave like GetStagedBlockId() until the next commit /// @@ -45,5 +43,11 @@ public interface IBulkBlockAccessor : IBlockAccessor /// void SetChunks(Vec2i chunkCoord, IWorldChunk[] chunksCol); void SetDecorsBulk(long chunkIndex, Dictionary newDecors); + + /// + /// Used to fix certain things like flowing water from the edge of a pasted schematic/selection when undone or /we delete is used + /// + /// + void PostCommitCleanup(List updatedBlocks); } } diff --git a/Common/API/IClassRegistryAPI.cs b/Common/API/IClassRegistryAPI.cs index c63cd761..09ec44d8 100644 --- a/Common/API/IClassRegistryAPI.cs +++ b/Common/API/IClassRegistryAPI.cs @@ -47,7 +47,7 @@ public interface IClassRegistryAPI /// /// Creates a entity instance from given entity type /// - /// + /// /// Entity CreateEntity(EntityProperties entityType); diff --git a/Common/API/IEventAPI.cs b/Common/API/IEventAPI.cs index 4425c6cf..63f2df2f 100644 --- a/Common/API/IEventAPI.cs +++ b/Common/API/IEventAPI.cs @@ -31,7 +31,9 @@ public enum EnumChunkDirtyReason /// /// Triggered immediately when the server loads a chunk column from disk or generates a new one, in the SupplyChunks thread (not the main thread) /// - /// + /// + /// + /// /// public delegate void ChunkColumnBeginLoadChunkThread(IServerMapChunk mapChunk, int chunkX, int chunkZ, IWorldChunk[] chunks); @@ -61,6 +63,7 @@ public enum EnumChunkDirtyReason /// Triggered just before a map region gets unloaded /// /// regionX and regionZ (multiply with region size to get block position) + /// public delegate void MapRegionUnloadDelegate(Vec2i mapCoord, IMapRegion region); @@ -148,20 +151,22 @@ public interface IEventAPI /// /// Calls given method after every given interval until unregistered. The engine may call your method slightly later since these event are handled only during fixed interval game ticks. /// - /// + /// /// + /// /// listenerId - long RegisterGameTickListener(Action OnGameTick, int millisecondInterval, int initialDelayOffsetMs = 0); + long RegisterGameTickListener(Action onGameTick, int millisecondInterval, int initialDelayOffsetMs = 0); /// /// Calls given method after every given interval until unregistered. The engine may call your method slightly later since these event are handled only during fixed interval game ticks. /// - /// + /// /// /// + /// /// listenerId - long RegisterGameTickListener(Action OnGameTick, BlockPos pos, int millisecondInterval, int initialDelayOffsetMs = 0); + long RegisterGameTickListener(Action onGameTick, BlockPos pos, int millisecondInterval, int initialDelayOffsetMs = 0); diff --git a/Common/API/IGameCalendar.cs b/Common/API/IGameCalendar.cs index 8321758d..0a552727 100644 --- a/Common/API/IGameCalendar.cs +++ b/Common/API/IGameCalendar.cs @@ -312,5 +312,10 @@ public interface IGameCalendar /// /// void SetSeasonOverride(float? seasonRel); + + /// + /// Can be used to adjust apparent time of day and season for rendering, e.g. to create a series of timelapse images; restore to 0 when done + /// + float Timelapse { get; set; } } } diff --git a/Common/API/ILogger.cs b/Common/API/ILogger.cs index 75eec5b5..046ecd19 100644 --- a/Common/API/ILogger.cs +++ b/Common/API/ILogger.cs @@ -164,9 +164,9 @@ static LoggerBase() { try { - throw new Exception("Exception for the logger to load some exception related info"); + throw new DummyLoggerException("Exception for the logger to load some exception related info"); } - catch (Exception ex) + catch (DummyLoggerException ex) { var stackTrace = new StackTrace(ex, true); var frame = stackTrace.GetFrame(0); @@ -269,4 +269,12 @@ public void Worldgen(Exception e) public void Worldgen(string message) => Log(EnumLogType.Worldgen, message, _emptyArgs); } + + public class DummyLoggerException : Exception + { + public DummyLoggerException(string message) : base(message) + { + + } + } } diff --git a/Common/API/IMiniDimension.cs b/Common/API/IMiniDimension.cs index dbc4ac5a..583ff033 100644 --- a/Common/API/IMiniDimension.cs +++ b/Common/API/IMiniDimension.cs @@ -1,7 +1,5 @@ -using System; -using Vintagestory.API.Common.Entities; +using Vintagestory.API.Common.Entities; using Vintagestory.API.MathTools; -using Vintagestory.API.Server; namespace Vintagestory.API.Common { diff --git a/Common/API/IWorldAccessor.cs b/Common/API/IWorldAccessor.cs index c03adfbd..a33e06f4 100644 --- a/Common/API/IWorldAccessor.cs +++ b/Common/API/IWorldAccessor.cs @@ -305,25 +305,25 @@ public interface IWorldAccessor Entity[] GetIntersectingEntities(BlockPos basePos, Cuboidf[] collisionBoxes, ActionConsumable matches = null); - /// - /// Find the nearest player to the given position - /// - /// x coordinate - /// y coordinate - /// z coordinate - /// ID of the nearest player + /// + /// Find the nearest player to the given position + /// + /// x coordinate + /// y coordinate + /// z coordinate + /// ID of the nearest player IPlayer NearestPlayer(double x, double y, double z); - /// - /// Gets a list of all online players. Warning: Also returns currently connecting player whose player data may not have been fully initialized. Check for player.ConnectionState to avoid these. - /// - /// Array containing the IDs of online players + /// + /// Gets a list of all online players. Warning: Also returns currently connecting player whose player data may not have been fully initialized. Check for player.ConnectionState to avoid these. + /// + /// Array containing the IDs of online players IPlayer[] AllOnlinePlayers { get; } - /// - /// Gets a list of all players that connected to this server at least once. When called client side you will receive the same as AllOnlinePlayers - /// - /// Array containing the IDs of online players + /// + /// Gets a list of all players that connected to this server at least once. When called client side you will receive the same as AllOnlinePlayers + /// + /// Array containing the IDs of online players IPlayer[] AllPlayers { get; } @@ -438,11 +438,11 @@ public interface IWorldAccessor /// /// The position of the block to take the color from /// The position where the particles should spawn - /// /// /// /// /// If this call is made on client and on server, set this to the causing playerUID to prevent double spawning. Essentially dualCall will spawn the particles on the client, and send it to all other players except source client + /// void SpawnCubeParticles(BlockPos blockPos, Vec3d pos, float radius, int quantity, float scale = 1f, IPlayer dualCallByPlayer = null, Vec3f velocity = null); @@ -455,6 +455,7 @@ public interface IWorldAccessor /// /// /// If this call is made on client and on server, set this to the causing playerUID to prevent double spawning. Essentially dualCall will spawn the particles on the client, and send it to all other players except source client + /// void SpawnCubeParticles(Vec3d pos, ItemStack item, float radius, int quantity, float scale = 1f, IPlayer dualCallByPlayer = null, Vec3f velocity = null); @@ -504,7 +505,7 @@ public interface IWorldAccessor /// /// /// - /// Can be used to ignore certain blocks. Return false to ignore + /// /// Can be used to ignore certain entities. Return false to ignore void RayTraceForSelection(Ray ray, ref BlockSelection blockSelection, ref EntitySelection entitySelection, BlockFilter filter = null, EntityFilter efilter = null); @@ -512,10 +513,11 @@ public interface IWorldAccessor /// /// Calls given method after every given interval until unregistered. The engine may call your method slightly later since these event are handled only during fixed interval game ticks. /// - /// + /// /// + /// /// listenerId - long RegisterGameTickListener(Action OnGameTick, int millisecondInterval, int initialDelayOffsetMs = 0); + long RegisterGameTickListener(Action onGameTick, int millisecondInterval, int initialDelayOffsetMs = 0); /// /// Removes a game tick listener @@ -621,6 +623,7 @@ public interface IWorldAccessor /// /// Retrieve a customized interface to access blocks in the loaded game world. Does not relight. On commit all touched blocks are updated at once. This method is currently used for the snow accumulation system /// + /// /// /// IBulkBlockAccessor GetBlockAccessorBulkMinimalUpdate(bool synchronize, bool debug = false); diff --git a/Common/API/IWorldChunk.cs b/Common/API/IWorldChunk.cs index 3f8cda2f..ec9db70a 100644 --- a/Common/API/IWorldChunk.cs +++ b/Common/API/IWorldChunk.cs @@ -41,20 +41,33 @@ public interface IChunkBlocks void SetFluid(int index3d, int value); int GetBlockId(int index3d, int layer); int GetFluid(int index3d); + /// /// Like get (i.e. this[]) but not threadsafe - only for use where setting and getting is guaranteed to be all on the same thread (e.g. during worldgen) /// /// - /// int GetBlockIdUnsafe(int index3d); + /// /// Enter a locked section for bulk block reads from this ChunkData, using Unsafe read methods /// void TakeBulkReadLock(); + /// /// Leave a locked section for bulk block reads from this ChunkData, using Unsafe read methods /// void ReleaseBulkReadLock(); + + /// + /// Does this chunk contain any examples of the specified block? + ///
(If the result is false, this is a very fast lookup because it quickly scans the blocks palette, not every block individually.) + ///
+ bool ContainsBlock(int blockId); + /// + /// Populates the list with all block IDs which are present in this chunk. The list may contain false positives (i.e. blocks which used to be here but were removed) so that's why it's called a "Fuzzy" list. + /// There will be no false negatives, therefore useful as a first-pass filter when scanning chunks for various types of block e.g. ITickable + /// + void FuzzyListBlockIds(List reusableList); } public interface IChunkLight @@ -216,11 +229,13 @@ public interface IWorldChunk /// void SetModdata(string key, T data); + /// /// Retrieve arbitrary, permantly stored mod data /// /// /// + /// /// T GetModdata(string key, T defaultValue = default(T)); @@ -260,8 +275,7 @@ public interface IWorldChunk /// /// Sets a decor block to the side of an existing block. Use air block (id 0) to remove a decor.
///
- /// - /// + /// /// /// /// False if there already exists a block in this position and facing @@ -272,14 +286,11 @@ public interface IWorldChunk /// /// Sets a decor block to a specific sub-position on the side of an existing block. Use air block (id 0) to remove a decor.
///
- /// - /// - /// /// + /// + /// /// False if there already exists a block in this position and facing bool SetDecor(Block block, int index3d, int decorIndex); -// bool SetDecor(IBlockAccessor blockAccessor, Block block, BlockPos pos, int decorIndex); - /// /// If allowed by a player action, removes all decors at given position and calls OnBrokenAsDecor() on all selected decors and drops the items that are returned from Block.GetDrops() @@ -290,7 +301,6 @@ public interface IWorldChunk /// If not null breaks only this part of the decor for give face. Requires side to be set. bool BreakDecor(IWorldAccessor world, BlockPos pos, BlockFacing side = null, int? decorIndex = null); - /// /// Removes a decor block from given position, saves a few cpu cycles by not calculating index3d /// diff --git a/Common/Assets/IAssetManager.cs b/Common/Assets/IAssetManager.cs index cdc8ce05..5a510cbc 100644 --- a/Common/Assets/IAssetManager.cs +++ b/Common/Assets/IAssetManager.cs @@ -29,7 +29,6 @@ public interface IAssetManager /// /// Adds a runtime asset to the game, curently used by ModCompatiblityUtil. Allows you do add an asset found at but loaded from path .Path. /// - /// /// /// void Add(AssetLocation path, IAsset asset); @@ -37,9 +36,9 @@ public interface IAssetManager /// /// Retrieves an asset from given path within the assets folder. Throws an exception when the asset does not exist. Remember to use lower case paths. /// - /// + /// /// - IAsset Get(string Path); + IAsset Get(string path); /// /// Retrieves an asset from given path within the assets folder. Throws an exception when the asset does not exist. Remember to use lower case paths. @@ -110,14 +109,14 @@ public interface IAssetManager /// /// Reloads all assets in given base location path. It returns the amount of the found locations. /// - /// + /// /// int Reload(AssetLocation baseLocation); /// /// Reloads all assets in given base location path. It returns the amount of the found locations. /// - /// + /// /// int Reload(AssetCategory category); diff --git a/Common/Assets/IAssetOrigin.cs b/Common/Assets/IAssetOrigin.cs index 82e5e20b..91d5c02d 100644 --- a/Common/Assets/IAssetOrigin.cs +++ b/Common/Assets/IAssetOrigin.cs @@ -13,34 +13,36 @@ public interface IAssetOrigin /// void LoadAsset(IAsset asset); - /// - /// Attempts to load the asset. Returns false if it fails. - /// - /// The asset to be loaded. + /// + /// Attempts to load the asset. Returns false if it fails. + /// + /// The asset to be loaded. /// bool TryLoadAsset(IAsset asset); /// /// Returns all assets of the given category which can be found in this origin /// - /// + /// + /// /// - List GetAssets(AssetCategory Category, bool shouldLoad = true); - - + List GetAssets(AssetCategory category, bool shouldLoad = true); + + /// /// Returns all assets of the given base location path which can be found in this origin /// /// + /// /// - List GetAssets(AssetLocation baseLocation, bool shouldLoad = true); - - + List GetAssets(AssetLocation baseLocation, bool shouldLoad = true); + + /// /// Resource packs are not allowed to affect gameplay /// /// - bool IsAllowedToAffectGameplay(); + bool IsAllowedToAffectGameplay(); } } \ No newline at end of file diff --git a/Common/Collectible/Block/Block.cs b/Common/Collectible/Block/Block.cs index d174a849..9eee0dff 100644 --- a/Common/Collectible/Block/Block.cs +++ b/Common/Collectible/Block/Block.cs @@ -480,7 +480,15 @@ public virtual bool DisplacesLiquids(IBlockAccessor blockAccess, BlockPos pos) { return SideSolid.SidesAndBase; } - + + /// + /// Called for example when deciding to render water edges at a position, or not + /// + public virtual bool SideIsSolid(BlockPos pos, int faceIndex) + { + return SideSolid[faceIndex]; + } + /// /// This method gets called when facecull mode is set to 'Callback'. Curently used for custom behaviors when merging ice /// @@ -614,9 +622,9 @@ public virtual bool DoEmitSideAo(IGeometryTester caller, BlockFacing facing) return (EmitSideAo & facing.Flag) != 0; } - public virtual bool DoEmitSideAoByFlag(IGeometryTester caller, Vec3iAndFacingFlags vec) + public virtual bool DoEmitSideAoByFlag(IGeometryTester caller, Vec3iAndFacingFlags vec, int flags) { - return (EmitSideAo & vec.OppositeFlags) != 0; + return (EmitSideAo & flags) != 0; } public virtual int GetLightAbsorption(IBlockAccessor blockAccessor, BlockPos pos) @@ -1283,7 +1291,7 @@ public virtual void OnBlockPlaced(IWorldAccessor world, BlockPos blockPos, ItemS /// - /// Called when any of it's 6 neighbour blocks has been changed + /// Called when any of its 6 neighbour blocks has been changed /// /// /// @@ -1349,7 +1357,8 @@ public virtual bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, /// /// /// - /// + /// + /// public virtual void Activate(IWorldAccessor world, Caller caller, BlockSelection blockSel, ITreeAttribute activationArgs = null) { @@ -1527,7 +1536,7 @@ public virtual bool OnFallOnto(IWorldAccessor world, BlockPos pos, Block block, /// public virtual bool ShouldReceiveClientParticleTicks(IWorldAccessor world, IPlayer player, BlockPos pos, out bool isWindAffected) { - bool result = true; + bool result = false; bool preventDefault = false; isWindAffected = false; @@ -1538,7 +1547,7 @@ public virtual bool ShouldReceiveClientParticleTicks(IWorldAccessor world, IPlay bool behaviorResult = behavior.ShouldReceiveClientParticleTicks(world, player, pos, ref handled); if (handled != EnumHandling.PassThrough) { - result &= behaviorResult; + result |= behaviorResult; preventDefault = true; } @@ -2460,6 +2469,7 @@ public virtual T GetBEBehavior(BlockPos pos) where T : BlockEntityBehavior /// 4. BlockEntityBehavior /// /// + /// /// /// public virtual T GetInterface(IWorldAccessor world, BlockPos pos) where T: class diff --git a/Common/Collectible/Block/BlockBehavior.cs b/Common/Collectible/Block/BlockBehavior.cs index 84eca1b0..63a85c2e 100644 --- a/Common/Collectible/Block/BlockBehavior.cs +++ b/Common/Collectible/Block/BlockBehavior.cs @@ -86,6 +86,7 @@ public virtual void OnNeighbourBlockChange(IWorldAccessor world, BlockPos pos, B /// /// /// + /// /// public virtual bool CanAttachBlockAt(IBlockAccessor world, Block block, BlockPos pos, BlockFacing blockFace, ref EnumHandling handling, Cuboidi attachmentArea = null) { diff --git a/Common/Collectible/Block/BlockDropItemStack.cs b/Common/Collectible/Block/BlockDropItemStack.cs index 90c19db0..8c89abf2 100644 --- a/Common/Collectible/Block/BlockDropItemStack.cs +++ b/Common/Collectible/Block/BlockDropItemStack.cs @@ -69,6 +69,7 @@ public BlockDropItemStack(ItemStack stack, float chance = 1) ///
/// /// + /// /// public bool Resolve(IWorldAccessor resolver, string sourceForErrorLogging, AssetLocation assetLoc) { diff --git a/Common/Collectible/Block/BlockEntity.cs b/Common/Collectible/Block/BlockEntity.cs index 1a0def7e..7f75225e 100644 --- a/Common/Collectible/Block/BlockEntity.cs +++ b/Common/Collectible/Block/BlockEntity.cs @@ -99,12 +99,13 @@ public virtual void CreateBehaviors(Block block, IWorldAccessor worldForResolve) /// /// Registers a game tick listener that does the disposing for you when the Block is removed /// - /// + /// /// + /// /// - public virtual long RegisterGameTickListener(Action OnGameTick, int millisecondInterval, int initialDelayOffsetMs = 0) + public virtual long RegisterGameTickListener(Action onGameTick, int millisecondInterval, int initialDelayOffsetMs = 0) { - long listenerId = Api.Event.RegisterGameTickListener(OnGameTick, millisecondInterval, initialDelayOffsetMs); + long listenerId = Api.Event.RegisterGameTickListener(onGameTick, millisecondInterval, initialDelayOffsetMs); if (TickHandlers == null) TickHandlers = new List(1); TickHandlers.Add(listenerId); return listenerId; @@ -347,6 +348,7 @@ public virtual void OnReceivedServerPacket(int packetid, byte[] data) /// When called on Client: Triggers a block changed event on the client, but will not redraw the block unless specified. /// /// When true, the block is also marked dirty and thus redrawn. When called serverside a dirty block packet is sent to the client for it to be redrawn + /// public virtual void MarkDirty(bool redrawOnClient = false, IPlayer skipPlayer = null) { if (Api == null) return; @@ -362,6 +364,7 @@ public virtual void MarkDirty(bool redrawOnClient = false, IPlayer skipPlayer = /// Called by the block info HUD for displaying additional information /// /// + /// /// public virtual void GetBlockInfo(IPlayer forPlayer, StringBuilder dsc) { @@ -390,14 +393,31 @@ public virtual void OnStoreCollectibleMappings(Dictionary bl /// Note: Some vanilla blocks resolve randomized contents in this method. /// Hint: Use itemstack.FixMapping() to do the job for you. /// + /// /// /// /// If you need some sort of randomness consistency accross an imported schematic, you can use this value + [Obsolete("Use the variant with resolveImports parameter")] public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed) + { + OnLoadCollectibleMappings(worldForNewMappings, oldBlockIdMapping, oldItemIdMapping, schematicSeed, true); + } + + /// + /// Called by the blockschematic loader so that you may fix any blockid/itemid mappings against the mapping of the savegame, if you store any collectibles in this blockentity. + /// Note: Some vanilla blocks resolve randomized contents in this method. + /// Hint: Use itemstack.FixMapping() to do the job for you. + /// + /// + /// + /// + /// If you need some sort of randomness consistency accross an imported schematic, you can use this value + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed, bool resolveImports) { foreach (var val in Behaviors) { - val.OnLoadCollectibleMappings(worldForNewMappings, oldBlockIdMapping, oldItemIdMapping, schematicSeed); + val.OnLoadCollectibleMappings(worldForNewMappings, oldBlockIdMapping, oldItemIdMapping, schematicSeed, resolveImports); } } @@ -431,9 +451,15 @@ public virtual bool OnTesselation(ITerrainMeshPool mesher, ITesselatorAPI tessTh /// /// /// If block.CustomBlockLayerHandler is true and the block is below the surface, this value is set - public virtual void OnPlacementBySchematic(Server.ICoreServerAPI api, IBlockAccessor blockAccessor, BlockPos pos, Dictionary> replaceBlocks, int centerrockblockid, Block layerBlock) + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + public virtual void OnPlacementBySchematic(Server.ICoreServerAPI api, IBlockAccessor blockAccessor, BlockPos pos, Dictionary> replaceBlocks, int centerrockblockid, Block layerBlock, bool resolveImports) { Pos = pos.Copy(); + + for (int i = 0; i < Behaviors.Count; i++) + { + Behaviors[i].OnPlacementBySchematic(api, blockAccessor, pos, replaceBlocks, centerrockblockid, layerBlock, resolveImports); + } } } diff --git a/Common/Collectible/Block/BlockEntityBehavior.cs b/Common/Collectible/Block/BlockEntityBehavior.cs index 4cce0dc7..5ee3ae62 100644 --- a/Common/Collectible/Block/BlockEntityBehavior.cs +++ b/Common/Collectible/Block/BlockEntityBehavior.cs @@ -1,21 +1,23 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using System.Collections.Generic; using System.Text; using Vintagestory.API.Client; using Vintagestory.API.Datastructures; using Vintagestory.API.MathTools; +using Vintagestory.API.Server; namespace Vintagestory.API.Common { - public class BlockEntityBehaviorType - { - [JsonProperty] - public string Name; - - [JsonProperty, JsonConverter(typeof(JsonAttributesConverter))] - public JsonObject properties; - } - + public class BlockEntityBehaviorType + { + [JsonProperty] + public string Name; + + [JsonProperty, JsonConverter(typeof(JsonAttributesConverter))] + public JsonObject properties; + } + /// /// Basic class for block entities - a data structures to hold custom information for blocks, e.g. for chests to hold it's contents /// @@ -51,6 +53,7 @@ public BlockEntityBehavior(BlockEntity blockentity) /// /// Called right after the block behavior was created /// + /// /// public virtual void Initialize(ICoreAPI api, JsonObject properties) { @@ -107,7 +110,13 @@ public virtual void OnStoreCollectibleMappings(Dictionary bl } + [Obsolete("Use the variant with resolveImports parameter")] public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed) + { + OnLoadCollectibleMappings(worldForNewMappings, oldItemIdMapping, oldItemIdMapping, schematicSeed, true); + } + + public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed, bool resolveImports) { } @@ -116,5 +125,10 @@ public virtual bool OnTesselation(ITerrainMeshPool mesher, ITesselatorAPI tessTh { return false; } + + public virtual void OnPlacementBySchematic(ICoreServerAPI api, IBlockAccessor blockAccessor, BlockPos pos, Dictionary> replaceBlocks, int centerrockblockid, Block layerBlock, bool resolveImports) + { + + } } } diff --git a/Common/Collectible/Block/BlockSchematic.cs b/Common/Collectible/Block/BlockSchematic.cs index af2bf0a9..a5f69e8a 100644 --- a/Common/Collectible/Block/BlockSchematic.cs +++ b/Common/Collectible/Block/BlockSchematic.cs @@ -1,1287 +1,1355 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using Vintagestory.API.Common.Entities; -using Vintagestory.API.Datastructures; -using Vintagestory.API.MathTools; -using Vintagestory.API.Server; - -namespace Vintagestory.API.Common -{ - public delegate int PlaceBlockDelegate(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta); - - public enum EnumReplaceMode - { - /// - /// Replace if new block replaceable value > old block replaceable value - /// - Replaceable, - /// - /// Replace always, no matter what blocks were there previously - /// - ReplaceAll, - /// - /// Replace always, no matter what blocks were there previously, but skip air blocks in the schematic - /// - ReplaceAllNoAir, - /// - /// Replace only air blocks - /// - ReplaceOnlyAir - } - - public enum EnumOrigin - { - StartPos = 0, - BottomCenter = 1, - TopCenter = 2, - MiddleCenter = 3 - } - - [JsonObject(MemberSerialization.OptIn)] - public class BlockSchematic - { - [JsonProperty] - public string GameVersion; - [JsonProperty] - public int SizeX; - [JsonProperty] - public int SizeY; - [JsonProperty] - public int SizeZ; - [JsonProperty] - public Dictionary BlockCodes = new Dictionary(); - [JsonProperty] - public Dictionary ItemCodes = new Dictionary(); - [JsonProperty] - public List Indices = new List(); - [JsonProperty] - public List BlockIds = new List(); - [JsonProperty] - public List DecorIndices = new List(); - [JsonProperty] - public List DecorIds = new List(); - [JsonProperty] - public Dictionary BlockEntities = new Dictionary(); - [JsonProperty] - public List Entities = new List(); - - [JsonProperty] - public EnumReplaceMode ReplaceMode = EnumReplaceMode.ReplaceAllNoAir; - - [JsonProperty] - public int EntranceRotation = -1; - - public Dictionary BlocksUnpacked = new Dictionary(); - public Dictionary FluidsLayerUnpacked = new Dictionary(); - public Dictionary BlockEntitiesUnpacked = new Dictionary(); - public List EntitiesUnpacked = new List(); - public Dictionary DecorsUnpacked = new Dictionary(); - public FastVec3i PackedOffset; - - protected Block fillerBlock; - protected Block pathwayBlock; - protected Block undergroundBlock; - - protected ushort empty = 0; - public bool OmitLiquids = false; - - public BlockFacing[] PathwaySides; - /// - /// Distance positions from bottom left corner of the schematic. Only the first door block. - /// - public BlockPos[] PathwayStarts; - /// - /// Distance from the bottom left door block, so the bottom left door block is always at 0,0,0 - /// - public BlockPos[][] PathwayOffsets; - - public BlockPos[] UndergroundCheckPositions; - - public virtual void Init(IBlockAccessor blockAccessor) - { - InitMetaBlocks(blockAccessor); - } - - public void InitMetaBlocks(IBlockAccessor blockAccessor) - { - fillerBlock = blockAccessor.GetBlock(new AssetLocation("meta-filler")); - pathwayBlock = blockAccessor.GetBlock(new AssetLocation("meta-pathway")); - undergroundBlock = blockAccessor.GetBlock(new AssetLocation("meta-underground")); - } - - /// - /// Loads the meta information for each block in the schematic. - /// - /// - /// - /// - public void LoadMetaInformationAndValidate(IBlockAccessor blockAccessor, IWorldAccessor worldForResolve, string fileNameForLogging) - { - List undergroundPositions = new List(); - Queue pathwayPositions = new Queue(); - - HashSet missingBlocks = new HashSet(); - - for (int i = 0; i < Indices.Count; i++) - { - uint index = Indices[i]; - int storedBlockid = BlockIds[i]; - - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - AssetLocation blockCode = BlockCodes[storedBlockid]; - Block newBlock = blockAccessor.GetBlock(blockCode); - - if (newBlock == null) missingBlocks.Add(blockCode); - - if (newBlock != pathwayBlock && newBlock != undergroundBlock) continue; - - BlockPos pos = new BlockPos(dx, dy, dz); - - if (newBlock == pathwayBlock) - { - pathwayPositions.Enqueue(pos); - } else - { - undergroundPositions.Add(pos); - } - } - - for (int i = 0; i < DecorIds.Count; i++) - { - int storedBlockid = DecorIds[i] & 0xFFFFFF; - AssetLocation blockCode = BlockCodes[storedBlockid]; - Block newBlock = blockAccessor.GetBlock(blockCode); - if (newBlock == null) missingBlocks.Add(blockCode); - } - - if (missingBlocks.Count > 0) - { - worldForResolve.Logger.Warning("Block schematic file {0} uses blocks that could no longer be found. These will turn into air blocks! (affected: {1})", fileNameForLogging, string.Join(",", missingBlocks)); - } - - HashSet missingItems = new HashSet(); - foreach (var val in ItemCodes) - { - if (worldForResolve.GetItem(val.Value) == null) - { - missingItems.Add(val.Value); - } - } - - if (missingItems.Count > 0) - { - worldForResolve.Logger.Warning("Block schematic file {0} uses items that could no longer be found. These will turn into unknown items! (affected: {1})", fileNameForLogging, string.Join(",", missingItems)); - } - - - UndergroundCheckPositions = undergroundPositions.ToArray(); - - - List> pathwayslist = new List>(); - - if (pathwayPositions.Count == 0) - { - this.PathwayStarts = new BlockPos[0]; - this.PathwayOffsets = new BlockPos[0][]; - this.PathwaySides = new BlockFacing[0]; - return; - } - - - while (pathwayPositions.Count > 0) - { - List pathway = new List() { pathwayPositions.Dequeue() }; - pathwayslist.Add(pathway); - - int i = pathwayPositions.Count; - - while (i-- > 0) - { - BlockPos pos = pathwayPositions.Dequeue(); - bool found = false; - - for (int j = 0; j < pathway.Count; j++) - { - BlockPos ppos = pathway[j]; - int distance = Math.Abs(pos.X - ppos.X) + Math.Abs(pos.Y - ppos.Y) + Math.Abs(pos.Z - ppos.Z); - - if (distance == 1) - { - found = true; - pathway.Add(pos); - break; - } - } - - if (!found) pathwayPositions.Enqueue(pos); - else i = pathwayPositions.Count; - } - } - - - - PathwayStarts = new BlockPos[pathwayslist.Count]; - PathwayOffsets = new BlockPos[pathwayslist.Count][]; - PathwaySides = new BlockFacing[pathwayslist.Count]; - - - for (int i = 0; i < PathwayStarts.Length; i++) - { - // Concept to determine on which side the door is: - // 1. Iterate over every pathway block - // 2. Calculate the vector between the schematic center point an the pathway block - // 3. Get the average vector by summing up + divide by count - // => this is now basically the centerpoint of the door! - // 4. This final vector can now be used to determine the block facing - - Vec3f dirToMiddle = new Vec3f(); - - List pathway = pathwayslist[i]; - - for (int j = 0; j < pathway.Count; j++) - { - BlockPos pos = pathway[j]; - dirToMiddle.X += pos.X - SizeX / 2f; - dirToMiddle.Y += pos.Y - SizeY / 2f; - dirToMiddle.Z += pos.Z - SizeZ / 2f; - } - - dirToMiddle.Normalize(); - - PathwaySides[i] = BlockFacing.FromNormal(dirToMiddle); - BlockPos start = PathwayStarts[i] = pathwayslist[i][0].Copy(); - - PathwayOffsets[i] = new BlockPos[pathwayslist[i].Count]; - - for (int j = 0; j < pathwayslist[i].Count; j++) - { - PathwayOffsets[i][j] = pathwayslist[i][j].Sub(start); - } - } - - } - - - - - /// - /// Adds an area to the schematic. - /// - /// The world the blocks are in - /// The start position of all the blocks. - /// The end position of all the blocks. - public virtual void AddArea(IWorldAccessor world, BlockPos start, BlockPos end) - { - BlockPos startPos = new BlockPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), Math.Min(start.Z, end.Z)); - BlockPos finalPos = new BlockPos(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y), Math.Max(start.Z, end.Z)); - - for (int x = startPos.X; x < finalPos.X; x++) - { - for (int y = startPos.Y; y < finalPos.Y; y++) - { - for (int z = startPos.Z; z < finalPos.Z; z++) - { - BlockPos pos = new BlockPos(x, y, z); - int blockid = world.BlockAccessor.GetBlock(pos, BlockLayersAccess.Solid).BlockId; - int fluidid = world.BlockAccessor.GetBlock(pos, BlockLayersAccess.Fluid).BlockId; - if (fluidid == blockid) blockid = 0; - if (OmitLiquids) fluidid = 0; - if (blockid == 0 && fluidid == 0) continue; - - BlocksUnpacked[pos] = blockid; - FluidsLayerUnpacked[pos] = fluidid; - - BlockEntity be = world.BlockAccessor.GetBlockEntity(pos); - if (be != null) - { - BlockEntitiesUnpacked[pos] = EncodeBlockEntityData(be); - be.OnStoreCollectibleMappings(BlockCodes, ItemCodes); - } - - Block[] decors = world.BlockAccessor.GetDecors(pos); - if (decors != null) - { - DecorsUnpacked[pos] = decors; - } - } - } - } - - EntitiesUnpacked.AddRange(world.GetEntitiesInsideCuboid(start, end, (e) => !(e is EntityPlayer))); - - foreach (var entity in EntitiesUnpacked) - { - entity.OnStoreCollectibleMappings(BlockCodes, ItemCodes); - } - } - - - public virtual bool Pack(IWorldAccessor world, BlockPos startPos) - { - Indices.Clear(); - BlockIds.Clear(); - BlockEntities.Clear(); - Entities.Clear(); - DecorIndices.Clear(); - DecorIds.Clear(); - SizeX = 0; - SizeY = 0; - SizeZ = 0; - - int minX = int.MaxValue, minY = int.MaxValue, minZ = int.MaxValue; - - foreach (var val in BlocksUnpacked) - { - minX = Math.Min(minX, val.Key.X); - minY = Math.Min(minY, val.Key.Y); - minZ = Math.Min(minZ, val.Key.Z); - - // Store relative position and the block id - int dx = val.Key.X - startPos.X; - int dy = val.Key.Y - startPos.Y; - int dz = val.Key.Z - startPos.Z; - - if (dx >= 1024 || dy >= 1024 || dz >= 1024) - { - world.Logger.Warning("Export format does not support areas larger than 1024 blocks in any direction. Will not pack."); - PackedOffset = new FastVec3i(0, 0, 0); - return false; - } - } - - foreach (var val in BlocksUnpacked) - { - int fluidid; - if (!FluidsLayerUnpacked.TryGetValue(val.Key, out fluidid)) fluidid = 0; - int blockid = val.Value; - if (blockid == 0 && fluidid == 0) continue; - - // Store a block mapping - if (blockid != 0) BlockCodes[blockid] = world.BlockAccessor.GetBlock(blockid).Code; - if (fluidid != 0) BlockCodes[fluidid] = world.BlockAccessor.GetBlock(fluidid).Code; - - // Store relative position and the block id - int dx = val.Key.X - minX; - int dy = val.Key.Y - minY; - int dz = val.Key.Z - minZ; - - SizeX = Math.Max(dx, SizeX); - SizeY = Math.Max(dy, SizeY); - SizeZ = Math.Max(dz, SizeZ); - - Indices.Add((uint)((dy << 20) | (dz << 10) | dx)); - if (fluidid == 0) - { - BlockIds.Add(blockid); - } - else if (blockid == 0) - { - BlockIds.Add(fluidid); - } - else // if both block layer and liquid layer are present (non zero), add this twice; placing code will place the liquidid blocks in the liquids layer - { - BlockIds.Add(blockid); - Indices.Add((uint)((dy << 20) | (dz << 10) | dx)); - BlockIds.Add(fluidid); - } - } - - foreach (var val in DecorsUnpacked) - { - // Store relative position and the block id - int dx = val.Key.X - minX; - int dy = val.Key.Y - minY; - int dz = val.Key.Z - minZ; - - SizeX = Math.Max(dx, SizeX); - SizeY = Math.Max(dy, SizeY); - SizeZ = Math.Max(dz, SizeZ); - - for (int i = 0; i < 6; i++) - { - // Store a block mapping - Block b = val.Value[i]; - if (b == null) continue; - - BlockCodes[b.BlockId] = b.Code; - DecorIndices.Add((uint)((dy << 20) | (dz << 10) | dx)); - DecorIds.Add((i << 24) + b.BlockId); - } - } - - // off-by-one problem as usual. A block at x=3 and x=4 means a sizex of 2 - SizeX++; - SizeY++; - SizeZ++; - - foreach(var val in BlockEntitiesUnpacked) - { - int dx = val.Key.X - minX; - int dy = val.Key.Y - minY; - int dz = val.Key.Z - minZ; - BlockEntities[(uint)((dy << 20) | (dz << 10) | dx)] = val.Value; - } - - BlockPos minPos = new BlockPos(minX, minY, minZ); - foreach (Entity e in EntitiesUnpacked) - { - using (MemoryStream ms = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(ms); - - writer.Write(world.ClassRegistry.GetEntityClassName(e.GetType())); - - e.WillExport(minPos); - e.ToBytes(writer, false); - e.DidImportOrExport(minPos); - - Entities.Add(Ascii85.Encode(ms.ToArray())); - } - } - - PackedOffset = new FastVec3i(minX - startPos.X, minY - startPos.Y, minZ - startPos.Z); - return true; - } - - /// - /// Will place all blocks using the configured replace mode. Note: If you use a revertable or bulk block accessor you will have to call PlaceBlockEntities() after the Commit() - /// - /// - /// - /// - /// - /// - public virtual int Place(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, bool replaceMetaBlocks = true) - { - int result = Place(blockAccessor, worldForCollectibleResolve, startPos, ReplaceMode, replaceMetaBlocks); - PlaceDecors(blockAccessor, startPos); - return result; - } - - /// - /// Will place all blocks using the supplied replace mode. Note: If you use a revertable or bulk block accessor you will have to call PlaceBlockEntities() after the Commit() - /// - /// - /// - /// - /// - /// - /// - public virtual int Place(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, EnumReplaceMode mode, bool replaceMetaBlocks = true) - { - BlockPos curPos = new BlockPos(); - int placed = 0; - - PlaceBlockDelegate handler = null; - switch (mode) - { - case EnumReplaceMode.ReplaceAll: - handler = PlaceReplaceAll; - - for (int dx = 0; dx < SizeX; dx++) - { - for (int dy = 0; dy < SizeY; dy++) - { - for (int dz = 0; dz < SizeZ; dz++) - { - curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); - blockAccessor.SetBlock(0, curPos); - } - } - } - break; - - case EnumReplaceMode.Replaceable: - handler = PlaceReplaceable; - break; - - case EnumReplaceMode.ReplaceAllNoAir: - handler = PlaceReplaceAllNoAir; - break; - - case EnumReplaceMode.ReplaceOnlyAir: - handler = PlaceReplaceOnlyAir; - break; - } - - for (int i = 0; i < Indices.Count; i++) - { - uint index = Indices[i]; - int storedBlockid = BlockIds[i]; - - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - AssetLocation blockCode = BlockCodes[storedBlockid]; - - Block newBlock = blockAccessor.GetBlock(blockCode); - - if (newBlock == null || (replaceMetaBlocks && newBlock == undergroundBlock)) continue; - - curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); - placed += handler(blockAccessor, curPos, newBlock, replaceMetaBlocks); - - - if (newBlock.LightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor) - { - Block oldBlock = blockAccessor.GetBlock(curPos); - ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId); - } - } - - if (!(blockAccessor is IBlockAccessorRevertable)) - { - PlaceEntitiesAndBlockEntities(blockAccessor, worldForCollectibleResolve, startPos, BlockCodes, ItemCodes); - } - - return placed; - } - - public virtual void PlaceDecors(IBlockAccessor blockAccessor, BlockPos startPos) - { - this.curPos.dimension = startPos.dimension; - for (int i = 0; i < DecorIndices.Count; i++) - { - uint index = DecorIndices[i]; - int posX = startPos.X + (int)(index & 0x1ff); - int posY = startPos.Y + (int)((index >> 20) & 0x1ff); - int posZ = startPos.Z + (int)((index >> 10) & 0x1ff); - int storedBlockid = DecorIds[i]; - PlaceOneDecor(blockAccessor, posX, posY, posZ, storedBlockid); - } - } - - public virtual void PlaceDecors(IBlockAccessor blockAccessor, BlockPos startPos, Rectanglei rect) - { - int i = -1; - foreach (uint index in DecorIndices) - { - i++; // increment i first, because we have various continue statements - - int posX = startPos.X + (int)(index & 0x1ff); - int posZ = startPos.Z + (int)((index >> 10) & 0x1ff); - if (!rect.Contains(posX, posZ)) continue; - - int posY = startPos.Y + (int)((index >> 20) & 0x1ff); - - int storedBlockid = DecorIds[i]; - PlaceOneDecor(blockAccessor, posX, posY, posZ, storedBlockid); - } - } - - BlockPos curPos = new BlockPos(); - private void PlaceOneDecor(IBlockAccessor blockAccessor, int posX, int posY, int posZ, int storedBlockid) - { - byte faceIndex = (byte)(storedBlockid >> 24); - if (faceIndex > 5) return; - BlockFacing face = BlockFacing.ALLFACES[faceIndex]; - storedBlockid &= 0xFFFFFF; - AssetLocation blockCode = BlockCodes[storedBlockid]; - - Block newBlock = blockAccessor.GetBlock(blockCode); - - if (newBlock == null) return; - - curPos.Set(posX, posY, posZ); - blockAccessor.SetDecor(newBlock, curPos, face); - } - - /// - /// Attempts to transform each block as they are placed in directions different from the schematic. - /// - /// - /// - /// - /// - public virtual void TransformWhilePacked(IWorldAccessor worldForResolve, EnumOrigin aroundOrigin, int angle, EnumAxis? flipAxis = null) - { - BlockPos startPos = new BlockPos(1024, 1024, 1024); - - BlocksUnpacked.Clear(); - FluidsLayerUnpacked.Clear(); - BlockEntitiesUnpacked.Clear(); - DecorsUnpacked.Clear(); - EntitiesUnpacked.Clear(); - - angle = GameMath.Mod(angle, 360); - if (angle == 0) return; - - if (EntranceRotation != -1) - { - EntranceRotation = GameMath.Mod(EntranceRotation + angle, 360); - } - - for (int i = 0; i < Indices.Count; i++) - { - uint index = Indices[i]; - int storedBlockid = BlockIds[i]; - - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - AssetLocation blockCode = BlockCodes[storedBlockid]; - - Block newBlock = worldForResolve.GetBlock(blockCode); - if (newBlock == null) - { - BlockEntities.Remove(index); - continue; - } - - if (flipAxis != null) - { - if (flipAxis == EnumAxis.Y) - { - dy = SizeY - dy; - - AssetLocation newCode = newBlock.GetVerticallyFlippedBlockCode(); - newBlock = worldForResolve.GetBlock(newCode); - } - - if (flipAxis == EnumAxis.X) - { - dx = SizeX - dx; - - AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); - newBlock = worldForResolve.GetBlock(newCode); - } - - if (flipAxis == EnumAxis.Z) - { - dz = SizeZ - dz; - - AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); - newBlock = worldForResolve.GetBlock(newCode); - } - } - - if (angle != 0) - { - AssetLocation newCode = newBlock.GetRotatedBlockCode(angle); - var rotBlock = worldForResolve.GetBlock(newCode); - if (rotBlock != null) - { - newBlock = rotBlock; - } else - { - worldForResolve.Logger.Warning("Schematic rotate: Unable to rotate block {0} - its GetRotatedBlockCode() method call returns an invalid block code: {1}! Will use unrotated variant.", blockCode, newCode); - } - } - - BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); - - if (newBlock.ForFluidsLayer) - { - FluidsLayerUnpacked[pos] = newBlock.BlockId; - } - else - { - BlocksUnpacked[pos] = newBlock.BlockId; - } - } - - - for (int i = 0; i < DecorIndices.Count; i++) - { - uint index = DecorIndices[i]; - int storedBlockid = DecorIds[i]; - byte faceIndex = (byte)(storedBlockid >> 24); - if (faceIndex > 5) continue; - BlockFacing face = BlockFacing.ALLFACES[faceIndex]; - - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - AssetLocation blockCode = BlockCodes[storedBlockid & 0xFFFFFF]; - - Block newBlock = worldForResolve.GetBlock(blockCode); - if (newBlock == null) - { - continue; - } - - if (flipAxis != null) - { - if (flipAxis == EnumAxis.Y) - { - dy = SizeY - dy; - - AssetLocation newCode = newBlock.GetVerticallyFlippedBlockCode(); - newBlock = worldForResolve.GetBlock(newCode); - if (face.IsVertical) face = face.Opposite; - } - - if (flipAxis == EnumAxis.X) - { - dx = SizeX - dx; - - AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); - newBlock = worldForResolve.GetBlock(newCode); - if (face.Axis == EnumAxis.X) face = face.Opposite; - } - - if (flipAxis == EnumAxis.Z) - { - dz = SizeZ - dz; - - AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); - newBlock = worldForResolve.GetBlock(newCode); - if (face.Axis == EnumAxis.Z) face = face.Opposite; - } - } - - if (angle != 0) - { - AssetLocation newCode = newBlock.GetRotatedBlockCode(angle); - newBlock = worldForResolve.GetBlock(newCode); - } - - BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); - - DecorsUnpacked.TryGetValue(pos, out Block[] decorsTmp); - if (decorsTmp == null) - { - decorsTmp = new Block[6]; - DecorsUnpacked[pos] = decorsTmp; - } - decorsTmp[face.Index] = newBlock; - } - - - foreach (var val in BlockEntities) - { - uint index = val.Key; - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - if (flipAxis == EnumAxis.Y) dy = SizeY - dy; - if (flipAxis == EnumAxis.X) dx = SizeX - dx; - if (flipAxis == EnumAxis.Z) dz = SizeZ - dz; - BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); - - string beData = val.Value; - - var block = worldForResolve.GetBlock(BlocksUnpacked[pos]); - string entityclass = block.EntityClass; - - if (entityclass != null) - { - BlockEntity be = worldForResolve.ClassRegistry.CreateBlockEntity(entityclass); - if (be is IRotatable) - { - be.Pos = pos; - be.CreateBehaviors(block, worldForResolve); - ITreeAttribute tree = DecodeBlockEntityData(beData); - - (be as IRotatable).OnTransformed(tree, angle, flipAxis); - beData = StringEncodeTreeAttribute(tree); - } - - BlockEntitiesUnpacked[pos] = beData; - } - } - - - foreach (string entityData in Entities) - { - using (MemoryStream ms = new MemoryStream(Ascii85.Decode(entityData))) - { - BinaryReader reader = new BinaryReader(ms); - - string className = reader.ReadString(); - Entity entity = worldForResolve.ClassRegistry.CreateEntity(className); - - entity.FromBytes(reader, false); - - var pos = entity.ServerPos; - - double offx = 0; - double offz = 0; - - if (aroundOrigin != EnumOrigin.StartPos) - { - offx = SizeX / 2.0; - offz = SizeZ / 2.0; - } - - pos.X -= offx; - pos.Z -= offz; - - var x = pos.X; - var z = pos.Z; - - switch (angle) - { - case 90: - pos.X = -z + offz; // I have no idea why i need to add offz/offx flipped here for entities, but not for blocks (-‸ლ) - pos.Z = x + offx; - break; - case 180: - pos.X = -x + offx; - pos.Z = -z + offz; - break; - case 270: - pos.X = z + offz; - pos.Z = -x + offx; - break; - } - - pos.Yaw -= angle * GameMath.DEG2RAD; - entity.Pos.Yaw -= angle * GameMath.DEG2RAD; - - entity.Pos.SetPos(pos); - entity.PositionBeforeFalling.X = pos.X; - entity.PositionBeforeFalling.Z = pos.Z; - - entity.DidImportOrExport(startPos); - - - EntitiesUnpacked.Add(entity); - } - } - - Pack(worldForResolve, startPos); - } - - private BlockPos getRotatedPos(EnumOrigin aroundOrigin, int angle, int dx, int dy, int dz) - { - if (aroundOrigin != EnumOrigin.StartPos) - { - dx -= SizeX / 2; - dz -= SizeZ / 2; - } - - BlockPos pos = new BlockPos(dx, dy, dz); - - // 90 deg: - // xNew = -yOld - // yNew = xOld - - // 180 deg: - // xNew = -xOld - // yNew = -yOld - - // 270 deg: - // xNew = yOld - // yNew = -xOld - - switch (angle) - { - case 90: - pos.Set(-dz, dy, dx); - break; - case 180: - pos.Set(-dx, dy, -dz); - break; - case 270: - pos.Set(dz, dy, -dx); - break; - } - - if (aroundOrigin != EnumOrigin.StartPos) - { - pos.X += SizeX / 2; - pos.Z += SizeZ / 2; - } - - return pos; - } - - - - - - - /// - /// Places all the entities and blocks in the schematic at the position. - /// - /// - /// - /// - /// - /// - /// - /// - /// - public void PlaceEntitiesAndBlockEntities(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, Dictionary blockCodes, Dictionary itemCodes, bool replaceBlockEntities = false, Dictionary> replaceBlocks = null, int centerrockblockid = 0, Dictionary layerBlockForBlockEntities = null) - { - BlockPos curPos = startPos.Copy(); - - int schematicSeed = worldForCollectibleResolve.Rand.Next(); - - foreach (var val in BlockEntities) - { - uint index = val.Key; - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); - - BlockEntity be = blockAccessor.GetBlockEntity(curPos); - - // Block entities need to be manually initialized for world gen block access - if ((be == null || replaceBlockEntities) && blockAccessor is IWorldGenBlockAccessor) - { - Block block = blockAccessor.GetBlock(curPos, BlockLayersAccess.Solid); - - if (block.EntityClass != null) - { - blockAccessor.SpawnBlockEntity(block.EntityClass, curPos); - be = blockAccessor.GetBlockEntity(curPos); - } - } - - if (be != null) - { - if (!replaceBlockEntities) - { - Block block = blockAccessor.GetBlock(curPos, BlockLayersAccess.Solid); - if (block.EntityClass != worldForCollectibleResolve.ClassRegistry.GetBlockEntityClass(be.GetType())) - { - worldForCollectibleResolve.Logger.Warning("Could not import block entity data for schematic at {0}. There is already {1}, expected {2}. Probably overlapping ruins.", curPos, be.GetType(), block.EntityClass); - continue; - } - } - - ITreeAttribute tree = DecodeBlockEntityData(val.Value); - tree.SetInt("posx", curPos.X); - tree.SetInt("posy", curPos.InternalY); - tree.SetInt("posz", curPos.Z); - - be.FromTreeAttributes(tree, worldForCollectibleResolve); - be.OnLoadCollectibleMappings(worldForCollectibleResolve, blockCodes, itemCodes, schematicSeed); - Block layerBlock = null; - layerBlockForBlockEntities?.TryGetValue(curPos, out layerBlock); - be.OnPlacementBySchematic(worldForCollectibleResolve.Api as ICoreServerAPI, blockAccessor, curPos, replaceBlocks, centerrockblockid, layerBlock); - if (!(blockAccessor is IWorldGenBlockAccessor)) be.MarkDirty(); - } - } - - foreach (string entityData in Entities) - { - using (MemoryStream ms = new MemoryStream(Ascii85.Decode(entityData))) - { - BinaryReader reader = new BinaryReader(ms); - - string className = reader.ReadString(); - Entity entity = worldForCollectibleResolve.ClassRegistry.CreateEntity(className); - - entity.FromBytes(reader, false); - entity.DidImportOrExport(startPos); - - // Not ideal but whatever - if (blockAccessor is IWorldGenBlockAccessor) - { - (blockAccessor as IWorldGenBlockAccessor).AddEntity(entity); - entity.OnInitialized += () => entity.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes, schematicSeed); - } else - { - if (entity.Properties != null) // Can be null if its a no longer existant mob type - { - worldForCollectibleResolve.SpawnEntity(entity); - entity.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes, schematicSeed); - } - } - } - } - } - - /// - /// Gets just the positions of the blocks. - /// - /// The origin point to start from - /// An array containing the BlockPos of each block in the area. - public virtual BlockPos[] GetJustPositions(BlockPos origin) - { - BlockPos[] positions = new BlockPos[Indices.Count]; - - for (int i = 0; i < Indices.Count; i++) - { - uint index = Indices[i]; - - int dx = (int)(index & 0x1ff); - int dy = (int)((index >> 20) & 0x1ff); - int dz = (int)((index >> 10) & 0x1ff); - - BlockPos pos = new BlockPos(dx, dy, dz); - positions[i] = pos.Add(origin); - } - - return positions; - } - - - /// - /// Gets the starting position of the schematic. - /// - /// - /// - /// - public virtual BlockPos GetStartPos(BlockPos pos, EnumOrigin origin) - { - return AdjustStartPos(pos.Copy(), origin); - } - - /// - /// Adjusts the starting position of the schemtic. - /// - /// - /// - /// - public virtual BlockPos AdjustStartPos(BlockPos startpos, EnumOrigin origin) - { - if (origin == EnumOrigin.TopCenter) - { - startpos.X -= SizeX / 2; - startpos.Y -= SizeY; - startpos.Z -= SizeZ / 2; - } - - if (origin == EnumOrigin.BottomCenter) - { - startpos.X -= SizeX / 2; - startpos.Z -= SizeZ / 2; - } - - if (origin == EnumOrigin.MiddleCenter) - { - startpos.X -= SizeX / 2; - startpos.Y -= SizeY / 2; - startpos.Z -= SizeZ / 2; - } - - return startpos; - } - - /// - /// Loads the schematic from a file. - /// - /// - /// - /// - public static BlockSchematic LoadFromFile(string infilepath, ref string error) - { - if (!File.Exists(infilepath) && File.Exists(infilepath + ".json")) - { - infilepath += ".json"; - } - - if (!File.Exists(infilepath)) - { - error = "Can't import " + infilepath + ", it does not exist"; - return null; - } - - BlockSchematic blockdata = null; - - try - { - using (TextReader textReader = new StreamReader(infilepath)) - { - blockdata = JsonConvert.DeserializeObject(textReader.ReadToEnd()); - textReader.Close(); - } - } - catch (Exception e) - { - error = "Failed loading " + infilepath + " : " + e.Message; - return null; - } - - return blockdata; - } - - /// - /// Loads a schematic from a string. - /// - /// - /// - /// - public static BlockSchematic LoadFromString(string jsoncode, ref string error) - { - try - { - return JsonConvert.DeserializeObject(jsoncode); - } - catch (Exception e) - { - error = "Failed loading schematic from json code : " + e.Message; - return null; - } - } - - /// - /// Saves a schematic to a file. - /// - /// - /// - public virtual string Save(string outfilepath) - { - this.GameVersion = API.Config.GameVersion.ShortGameVersion; - - if (!outfilepath.EndsWith(".json")) - { - outfilepath += ".json"; - } - - try - { - using (TextWriter textWriter = new StreamWriter(outfilepath)) - { - textWriter.Write(JsonConvert.SerializeObject(this, Formatting.None)); - textWriter.Close(); - } - } - catch (IOException e) - { - return "Failed exporting: " + e.Message; - } - - return null; - } - - - public virtual string ToJson() - { - this.GameVersion = API.Config.GameVersion.ShortGameVersion; - return JsonConvert.SerializeObject(this, Formatting.None); - } - - - - /// - /// Exports the block entity data to a string. - /// - /// - /// - public virtual string EncodeBlockEntityData(BlockEntity be) - { - TreeAttribute tree = new TreeAttribute(); - be.ToTreeAttributes(tree); - - return StringEncodeTreeAttribute(tree); - } - - /// - /// Exports the tree attribute data to a string. - /// - /// - /// - public virtual string StringEncodeTreeAttribute(ITreeAttribute tree) - { - byte[] data; - using (MemoryStream ms = new MemoryStream()) - { - BinaryWriter writer = new BinaryWriter(ms); - tree.ToBytes(writer); - data = ms.ToArray(); - } - - return Ascii85.Encode(data); - } - - - /// - /// Imports the tree data from a string. - /// - /// - /// - public virtual TreeAttribute DecodeBlockEntityData(string data) - { - byte[] bedata = Ascii85.Decode(data); - - TreeAttribute tree = new TreeAttribute(); - - using (MemoryStream ms = new MemoryStream(bedata)) - { - BinaryReader reader = new BinaryReader(ms); - tree.FromBytes(reader); - } - - return tree; - } - - - - - - protected virtual int PlaceReplaceAll(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) - { - // In BlockAccessorWorldGen, SetBlock run on liquids does not clear solid blocks, we have to clear them manually - if (newBlock.ForFluidsLayer) blockAccessor.SetBlock(0, pos, BlockLayersAccess.Solid); - - blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); - return 1; - } - - protected virtual int PlaceReplaceable(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) - { - if (newBlock.ForFluidsLayer || blockAccessor.GetBlock(pos, BlockLayersAccess.MostSolid).Replaceable > newBlock.Replaceable) - { - blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); - return 1; - } - return 0; - } - - protected virtual int PlaceReplaceAllNoAir(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) - { - if (newBlock.BlockId != 0) - { - // In BlockAccessorWorldGen, SetBlock run on liquids does not clear solid blocks, we have to clear them manually - if (newBlock.ForFluidsLayer) blockAccessor.SetBlock(0, pos, BlockLayersAccess.Solid); - - blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); - return 1; - } - return 0; - } - - protected virtual int PlaceReplaceOnlyAir(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) - { - Block oldBlock = blockAccessor.GetMostSolidBlock(pos); - if (oldBlock.BlockId == 0) - { - blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); - return 1; - } - return 0; - } - - - - /// - /// Makes a deep copy of the packed schematic. Unpacked data and loaded meta information is not cloned. - /// - /// - public virtual BlockSchematic ClonePacked() - { - BlockSchematic cloned = new BlockSchematic(); - cloned.SizeX = SizeX; - cloned.SizeY = SizeY; - cloned.SizeZ = SizeZ; - cloned.GameVersion = GameVersion; - cloned.BlockCodes = new Dictionary(BlockCodes); - cloned.ItemCodes = new Dictionary(ItemCodes); - cloned.Indices = new List(Indices); - cloned.BlockIds = new List(BlockIds); - cloned.BlockEntities = new Dictionary(BlockEntities); - cloned.Entities = new List(Entities); - cloned.ReplaceMode = ReplaceMode; - cloned.EntranceRotation = EntranceRotation; - cloned.DecorIndices = new List(DecorIndices); - cloned.DecorIds = new List(DecorIds); - return cloned; - } - } +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using ProperVersion; +using Vintagestory.API.Common.Entities; +using Vintagestory.API.Datastructures; +using Vintagestory.API.MathTools; +using Vintagestory.API.Server; + +namespace Vintagestory.API.Common +{ + public delegate int PlaceBlockDelegate(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta); + + public enum EnumReplaceMode + { + /// + /// Replace if new block replaceable value > old block replaceable value + /// + Replaceable, + /// + /// Replace always, no matter what blocks were there previously + /// + ReplaceAll, + /// + /// Replace always, no matter what blocks were there previously, but skip air blocks in the schematic + /// + ReplaceAllNoAir, + /// + /// Replace only air blocks + /// + ReplaceOnlyAir + } + + public enum EnumOrigin + { + StartPos = 0, + BottomCenter = 1, + TopCenter = 2, + MiddleCenter = 3 + } + + [JsonObject(MemberSerialization.OptIn)] + public class BlockSchematic + { + [JsonProperty] + public string GameVersion; + [JsonProperty] + public int SizeX; + [JsonProperty] + public int SizeY; + [JsonProperty] + public int SizeZ; + [JsonProperty] + public Dictionary BlockCodes = new Dictionary(); + [JsonProperty] + public Dictionary ItemCodes = new Dictionary(); + [JsonProperty] + public List Indices = new List(); + [JsonProperty] + public List BlockIds = new List(); + [JsonProperty] + public List DecorIndices = new List(); + [JsonProperty] + public List DecorIds = new List(); + [JsonProperty] + public Dictionary BlockEntities = new Dictionary(); + [JsonProperty] + public List Entities = new List(); + + [JsonProperty] + public EnumReplaceMode ReplaceMode = EnumReplaceMode.ReplaceAllNoAir; + + [JsonProperty] + public int EntranceRotation = -1; + + public Dictionary BlocksUnpacked = new Dictionary(); + public Dictionary FluidsLayerUnpacked = new Dictionary(); + public Dictionary BlockEntitiesUnpacked = new Dictionary(); + public List EntitiesUnpacked = new List(); + public Dictionary DecorsUnpacked = new Dictionary(); + public FastVec3i PackedOffset; + + protected Block fillerBlock; + protected Block pathwayBlock; + protected Block undergroundBlock; + + protected ushort empty = 0; + public bool OmitLiquids = false; + + public BlockFacing[] PathwaySides; + /// + /// Distance positions from bottom left corner of the schematic. Only the first door block. + /// + public BlockPos[] PathwayStarts; + /// + /// Distance from the bottom left door block, so the bottom left door block is always at 0,0,0 + /// + public BlockPos[][] PathwayOffsets; + + public BlockPos[] UndergroundCheckPositions; + + /// + /// Set by the RemapperAssistant in OnFinalizeAssets + /// + public static Dictionary> BlockRemaps { get; set; } + + /// + /// Set by the RemapperAssistant in OnFinalizeAssets + /// + public static Dictionary> ItemRemaps { get; set; } + + public virtual void Init(IBlockAccessor blockAccessor) + { + SemVer.TryParse(Config.GameVersion.OverallVersion, out var currentVersion); + SemVer.TryParse(GameVersion ?? "0.0.0", out var schematicVersion); + + if (schematicVersion < currentVersion) + { + Remap(); + } + + InitMetaBlocks(blockAccessor); + } + + private void Remap() + { + // now do block remapping + foreach (var map in BlockRemaps) + { + foreach (var blockCode in BlockCodes) + { + if (map.Value.TryGetValue(blockCode.Value.Path, out var newBlockCode)) + { + BlockCodes[blockCode.Key] = new AssetLocation(newBlockCode); + } + } + } + + // now do item remapping + foreach (var map in ItemRemaps) + { + foreach (var itemCode in ItemCodes) + { + if (map.Value.TryGetValue(itemCode.Value.Path, out var newItemCode)) + { + ItemCodes[itemCode.Key] = new AssetLocation(newItemCode); + } + } + } + } + + public void InitMetaBlocks(IBlockAccessor blockAccessor) + { + fillerBlock = blockAccessor.GetBlock(new AssetLocation("meta-filler")); + pathwayBlock = blockAccessor.GetBlock(new AssetLocation("meta-pathway")); + undergroundBlock = blockAccessor.GetBlock(new AssetLocation("meta-underground")); + } + + /// + /// Loads the meta information for each block in the schematic. + /// + /// + /// + /// + public void LoadMetaInformationAndValidate(IBlockAccessor blockAccessor, IWorldAccessor worldForResolve, string fileNameForLogging) + { + List undergroundPositions = new List(); + Queue pathwayPositions = new Queue(); + + HashSet missingBlocks = new HashSet(); + + for (int i = 0; i < Indices.Count; i++) + { + uint index = Indices[i]; + int storedBlockid = BlockIds[i]; + + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + AssetLocation blockCode = BlockCodes[storedBlockid]; + Block newBlock = blockAccessor.GetBlock(blockCode); + + if (newBlock == null) missingBlocks.Add(blockCode); + + if (newBlock != pathwayBlock && newBlock != undergroundBlock) continue; + + BlockPos pos = new BlockPos(dx, dy, dz); + + if (newBlock == pathwayBlock) + { + pathwayPositions.Enqueue(pos); + } else + { + undergroundPositions.Add(pos); + } + } + + for (int i = 0; i < DecorIds.Count; i++) + { + int storedBlockid = DecorIds[i] & 0xFFFFFF; + AssetLocation blockCode = BlockCodes[storedBlockid]; + Block newBlock = blockAccessor.GetBlock(blockCode); + if (newBlock == null) missingBlocks.Add(blockCode); + } + + if (missingBlocks.Count > 0) + { + worldForResolve.Logger.Warning("Block schematic file {0} uses blocks that could no longer be found. These will turn into air blocks! (affected: {1})", fileNameForLogging, string.Join(",", missingBlocks)); + } + + HashSet missingItems = new HashSet(); + foreach (var val in ItemCodes) + { + if (worldForResolve.GetItem(val.Value) == null) + { + missingItems.Add(val.Value); + } + } + + if (missingItems.Count > 0) + { + worldForResolve.Logger.Warning("Block schematic file {0} uses items that could no longer be found. These will turn into unknown items! (affected: {1})", fileNameForLogging, string.Join(",", missingItems)); + } + + + UndergroundCheckPositions = undergroundPositions.ToArray(); + + + List> pathwayslist = new List>(); + + if (pathwayPositions.Count == 0) + { + this.PathwayStarts = new BlockPos[0]; + this.PathwayOffsets = new BlockPos[0][]; + this.PathwaySides = new BlockFacing[0]; + return; + } + + + while (pathwayPositions.Count > 0) + { + List pathway = new List() { pathwayPositions.Dequeue() }; + pathwayslist.Add(pathway); + + int i = pathwayPositions.Count; + + while (i-- > 0) + { + BlockPos pos = pathwayPositions.Dequeue(); + bool found = false; + + for (int j = 0; j < pathway.Count; j++) + { + BlockPos ppos = pathway[j]; + int distance = Math.Abs(pos.X - ppos.X) + Math.Abs(pos.Y - ppos.Y) + Math.Abs(pos.Z - ppos.Z); + + if (distance == 1) + { + found = true; + pathway.Add(pos); + break; + } + } + + if (!found) pathwayPositions.Enqueue(pos); + else i = pathwayPositions.Count; + } + } + + + + PathwayStarts = new BlockPos[pathwayslist.Count]; + PathwayOffsets = new BlockPos[pathwayslist.Count][]; + PathwaySides = new BlockFacing[pathwayslist.Count]; + + + for (int i = 0; i < PathwayStarts.Length; i++) + { + // Concept to determine on which side the door is: + // 1. Iterate over every pathway block + // 2. Calculate the vector between the schematic center point an the pathway block + // 3. Get the average vector by summing up + divide by count + // => this is now basically the centerpoint of the door! + // 4. This final vector can now be used to determine the block facing + + Vec3f dirToMiddle = new Vec3f(); + + List pathway = pathwayslist[i]; + + for (int j = 0; j < pathway.Count; j++) + { + BlockPos pos = pathway[j]; + dirToMiddle.X += pos.X - SizeX / 2f; + dirToMiddle.Y += pos.Y - SizeY / 2f; + dirToMiddle.Z += pos.Z - SizeZ / 2f; + } + + dirToMiddle.Normalize(); + + PathwaySides[i] = BlockFacing.FromNormal(dirToMiddle); + BlockPos start = PathwayStarts[i] = pathwayslist[i][0].Copy(); + + PathwayOffsets[i] = new BlockPos[pathwayslist[i].Count]; + + for (int j = 0; j < pathwayslist[i].Count; j++) + { + PathwayOffsets[i][j] = pathwayslist[i][j].Sub(start); + } + } + + } + + + + + /// + /// Adds an area to the schematic. + /// + /// The world the blocks are in + /// The start position of all the blocks. + /// The end position of all the blocks. + public virtual void AddArea(IWorldAccessor world, BlockPos start, BlockPos end) + { + BlockPos startPos = new BlockPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), Math.Min(start.Z, end.Z)); + BlockPos finalPos = new BlockPos(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y), Math.Max(start.Z, end.Z)); + + for (int x = startPos.X; x < finalPos.X; x++) + { + for (int y = startPos.Y; y < finalPos.Y; y++) + { + for (int z = startPos.Z; z < finalPos.Z; z++) + { + BlockPos pos = new BlockPos(x, y, z); + int blockid = world.BlockAccessor.GetBlock(pos, BlockLayersAccess.Solid).BlockId; + int fluidid = world.BlockAccessor.GetBlock(pos, BlockLayersAccess.Fluid).BlockId; + if (fluidid == blockid) blockid = 0; + if (OmitLiquids) fluidid = 0; + if (blockid == 0 && fluidid == 0) continue; + + BlocksUnpacked[pos] = blockid; + FluidsLayerUnpacked[pos] = fluidid; + + BlockEntity be = world.BlockAccessor.GetBlockEntity(pos); + if (be != null) + { + BlockEntitiesUnpacked[pos] = EncodeBlockEntityData(be); + be.OnStoreCollectibleMappings(BlockCodes, ItemCodes); + } + + Block[] decors = world.BlockAccessor.GetDecors(pos); + if (decors != null) + { + DecorsUnpacked[pos] = decors; + } + } + } + } + + EntitiesUnpacked.AddRange(world.GetEntitiesInsideCuboid(start, end, (e) => !(e is EntityPlayer))); + + foreach (var entity in EntitiesUnpacked) + { + entity.OnStoreCollectibleMappings(BlockCodes, ItemCodes); + } + } + + + public virtual bool Pack(IWorldAccessor world, BlockPos startPos) + { + Indices.Clear(); + BlockIds.Clear(); + BlockEntities.Clear(); + Entities.Clear(); + DecorIndices.Clear(); + DecorIds.Clear(); + SizeX = 0; + SizeY = 0; + SizeZ = 0; + + int minX = int.MaxValue, minY = int.MaxValue, minZ = int.MaxValue; + + foreach (var val in BlocksUnpacked) + { + minX = Math.Min(minX, val.Key.X); + minY = Math.Min(minY, val.Key.Y); + minZ = Math.Min(minZ, val.Key.Z); + + // Store relative position and the block id + int dx = val.Key.X - startPos.X; + int dy = val.Key.Y - startPos.Y; + int dz = val.Key.Z - startPos.Z; + + if (dx >= 1024 || dy >= 1024 || dz >= 1024) + { + world.Logger.Warning("Export format does not support areas larger than 1024 blocks in any direction. Will not pack."); + PackedOffset = new FastVec3i(0, 0, 0); + return false; + } + } + + foreach (var val in BlocksUnpacked) + { + int fluidid; + if (!FluidsLayerUnpacked.TryGetValue(val.Key, out fluidid)) fluidid = 0; + int blockid = val.Value; + if (blockid == 0 && fluidid == 0) continue; + + // Store a block mapping + if (blockid != 0) BlockCodes[blockid] = world.BlockAccessor.GetBlock(blockid).Code; + if (fluidid != 0) BlockCodes[fluidid] = world.BlockAccessor.GetBlock(fluidid).Code; + + // Store relative position and the block id + int dx = val.Key.X - minX; + int dy = val.Key.Y - minY; + int dz = val.Key.Z - minZ; + + SizeX = Math.Max(dx, SizeX); + SizeY = Math.Max(dy, SizeY); + SizeZ = Math.Max(dz, SizeZ); + + Indices.Add((uint)((dy << 20) | (dz << 10) | dx)); + if (fluidid == 0) + { + BlockIds.Add(blockid); + } + else if (blockid == 0) + { + BlockIds.Add(fluidid); + } + else // if both block layer and liquid layer are present (non zero), add this twice; placing code will place the liquidid blocks in the liquids layer + { + BlockIds.Add(blockid); + Indices.Add((uint)((dy << 20) | (dz << 10) | dx)); + BlockIds.Add(fluidid); + } + } + + // also export fluid locks that do not have any other blocks in the same position + foreach (var (pos, blockId) in FluidsLayerUnpacked) + { + if (BlocksUnpacked.ContainsKey(pos)) continue; + + // Store a block mapping + if (blockId != 0) BlockCodes[blockId] = world.BlockAccessor.GetBlock(blockId).Code; + + // Store relative position and the block id + int dx = pos.X - minX; + int dy = pos.Y - minY; + int dz = pos.Z - minZ; + + SizeX = Math.Max(dx, SizeX); + SizeY = Math.Max(dy, SizeY); + SizeZ = Math.Max(dz, SizeZ); + + Indices.Add((uint)((dy << 20) | (dz << 10) | dx)); + BlockIds.Add(blockId); + } + + foreach (var val in DecorsUnpacked) + { + // Store relative position and the block id + int dx = val.Key.X - minX; + int dy = val.Key.Y - minY; + int dz = val.Key.Z - minZ; + + SizeX = Math.Max(dx, SizeX); + SizeY = Math.Max(dy, SizeY); + SizeZ = Math.Max(dz, SizeZ); + + for (int i = 0; i < 6; i++) + { + // Store a block mapping + Block b = val.Value[i]; + if (b == null) continue; + + BlockCodes[b.BlockId] = b.Code; + DecorIndices.Add((uint)((dy << 20) | (dz << 10) | dx)); + DecorIds.Add((i << 24) + b.BlockId); + } + } + + // off-by-one problem as usual. A block at x=3 and x=4 means a sizex of 2 + SizeX++; + SizeY++; + SizeZ++; + + foreach(var val in BlockEntitiesUnpacked) + { + int dx = val.Key.X - minX; + int dy = val.Key.Y - minY; + int dz = val.Key.Z - minZ; + BlockEntities[(uint)((dy << 20) | (dz << 10) | dx)] = val.Value; + } + + BlockPos minPos = new BlockPos(minX, minY, minZ); + foreach (Entity e in EntitiesUnpacked) + { + using (MemoryStream ms = new MemoryStream()) + { + BinaryWriter writer = new BinaryWriter(ms); + + writer.Write(world.ClassRegistry.GetEntityClassName(e.GetType())); + + e.WillExport(minPos); + e.ToBytes(writer, false); + e.DidImportOrExport(minPos); + + Entities.Add(Ascii85.Encode(ms.ToArray())); + } + } + + PackedOffset = new FastVec3i(minX - startPos.X, minY - startPos.Y, minZ - startPos.Z); + return true; + } + + /// + /// Will place all blocks using the configured replace mode. Note: If you use a revertable or bulk block accessor you will have to call PlaceBlockEntities() after the Commit() + /// + /// + /// + /// + /// + /// + public virtual int Place(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, bool replaceMetaBlocks = true) + { + int result = Place(blockAccessor, worldForCollectibleResolve, startPos, ReplaceMode, replaceMetaBlocks); + PlaceDecors(blockAccessor, startPos); + return result; + } + + /// + /// Will place all blocks using the supplied replace mode. Note: If you use a revertable or bulk block accessor you will have to call PlaceBlockEntities() after the Commit() + /// + /// + /// + /// + /// + /// + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + /// + public virtual int Place(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, EnumReplaceMode mode, bool replaceMetaBlocks = true) + { + BlockPos curPos = new BlockPos(); + int placed = 0; + + PlaceBlockDelegate handler = null; + switch (mode) + { + case EnumReplaceMode.ReplaceAll: + handler = PlaceReplaceAll; + + for (int dx = 0; dx < SizeX; dx++) + { + for (int dy = 0; dy < SizeY; dy++) + { + for (int dz = 0; dz < SizeZ; dz++) + { + curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); + blockAccessor.SetBlock(0, curPos); + } + } + } + break; + + case EnumReplaceMode.Replaceable: + handler = PlaceReplaceable; + break; + + case EnumReplaceMode.ReplaceAllNoAir: + handler = PlaceReplaceAllNoAir; + break; + + case EnumReplaceMode.ReplaceOnlyAir: + handler = PlaceReplaceOnlyAir; + break; + } + + for (int i = 0; i < Indices.Count; i++) + { + uint index = Indices[i]; + int storedBlockid = BlockIds[i]; + + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + AssetLocation blockCode = BlockCodes[storedBlockid]; + + Block newBlock = blockAccessor.GetBlock(blockCode); + + if (newBlock == null || (replaceMetaBlocks && newBlock == undergroundBlock)) continue; + + curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); + placed += handler(blockAccessor, curPos, newBlock, replaceMetaBlocks); + + + if (newBlock.LightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor) + { + Block oldBlock = blockAccessor.GetBlock(curPos); + ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId); + } + } + + if (!(blockAccessor is IBlockAccessorRevertable)) + { + PlaceEntitiesAndBlockEntities(blockAccessor, worldForCollectibleResolve, startPos, BlockCodes, ItemCodes, false, null, 0 , null, replaceMetaBlocks); + } + + return placed; + } + + public virtual void PlaceDecors(IBlockAccessor blockAccessor, BlockPos startPos) + { + this.curPos.dimension = startPos.dimension; + for (int i = 0; i < DecorIndices.Count; i++) + { + uint index = DecorIndices[i]; + int posX = startPos.X + (int)(index & 0x1ff); + int posY = startPos.Y + (int)((index >> 20) & 0x1ff); + int posZ = startPos.Z + (int)((index >> 10) & 0x1ff); + int storedBlockid = DecorIds[i]; + PlaceOneDecor(blockAccessor, posX, posY, posZ, storedBlockid); + } + } + + public virtual void PlaceDecors(IBlockAccessor blockAccessor, BlockPos startPos, Rectanglei rect) + { + int i = -1; + foreach (uint index in DecorIndices) + { + i++; // increment i first, because we have various continue statements + + int posX = startPos.X + (int)(index & 0x1ff); + int posZ = startPos.Z + (int)((index >> 10) & 0x1ff); + if (!rect.Contains(posX, posZ)) continue; + + int posY = startPos.Y + (int)((index >> 20) & 0x1ff); + + int storedBlockid = DecorIds[i]; + PlaceOneDecor(blockAccessor, posX, posY, posZ, storedBlockid); + } + } + + BlockPos curPos = new BlockPos(); + private void PlaceOneDecor(IBlockAccessor blockAccessor, int posX, int posY, int posZ, int storedBlockid) + { + byte faceIndex = (byte)(storedBlockid >> 24); + if (faceIndex > 5) return; + BlockFacing face = BlockFacing.ALLFACES[faceIndex]; + storedBlockid &= 0xFFFFFF; + AssetLocation blockCode = BlockCodes[storedBlockid]; + + Block newBlock = blockAccessor.GetBlock(blockCode); + + if (newBlock == null) return; + + curPos.Set(posX, posY, posZ); + blockAccessor.SetDecor(newBlock, curPos, face); + } + + /// + /// Attempts to transform each block as they are placed in directions different from the schematic. + /// + /// + /// + /// + /// + public virtual void TransformWhilePacked(IWorldAccessor worldForResolve, EnumOrigin aroundOrigin, int angle, EnumAxis? flipAxis = null) + { + BlockPos startPos = new BlockPos(1024, 1024, 1024); + + BlocksUnpacked.Clear(); + FluidsLayerUnpacked.Clear(); + BlockEntitiesUnpacked.Clear(); + DecorsUnpacked.Clear(); + EntitiesUnpacked.Clear(); + + angle = GameMath.Mod(angle, 360); + if (angle == 0) return; + + if (EntranceRotation != -1) + { + EntranceRotation = GameMath.Mod(EntranceRotation + angle, 360); + } + + for (int i = 0; i < Indices.Count; i++) + { + uint index = Indices[i]; + int storedBlockid = BlockIds[i]; + + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + AssetLocation blockCode = BlockCodes[storedBlockid]; + + Block newBlock = worldForResolve.GetBlock(blockCode); + if (newBlock == null) + { + BlockEntities.Remove(index); + continue; + } + + if (flipAxis != null) + { + if (flipAxis == EnumAxis.Y) + { + dy = SizeY - dy; + + AssetLocation newCode = newBlock.GetVerticallyFlippedBlockCode(); + newBlock = worldForResolve.GetBlock(newCode); + } + + if (flipAxis == EnumAxis.X) + { + dx = SizeX - dx; + + AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); + newBlock = worldForResolve.GetBlock(newCode); + } + + if (flipAxis == EnumAxis.Z) + { + dz = SizeZ - dz; + + AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); + newBlock = worldForResolve.GetBlock(newCode); + } + } + + if (angle != 0) + { + AssetLocation newCode = newBlock.GetRotatedBlockCode(angle); + var rotBlock = worldForResolve.GetBlock(newCode); + if (rotBlock != null) + { + newBlock = rotBlock; + } else + { + worldForResolve.Logger.Warning("Schematic rotate: Unable to rotate block {0} - its GetRotatedBlockCode() method call returns an invalid block code: {1}! Will use unrotated variant.", blockCode, newCode); + } + } + + BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); + + if (newBlock.ForFluidsLayer) + { + FluidsLayerUnpacked[pos] = newBlock.BlockId; + } + else + { + BlocksUnpacked[pos] = newBlock.BlockId; + } + } + + + for (int i = 0; i < DecorIndices.Count; i++) + { + uint index = DecorIndices[i]; + int storedBlockid = DecorIds[i]; + byte faceIndex = (byte)(storedBlockid >> 24); + if (faceIndex > 5) continue; + BlockFacing face = BlockFacing.ALLFACES[faceIndex]; + + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + AssetLocation blockCode = BlockCodes[storedBlockid & 0xFFFFFF]; + + Block newBlock = worldForResolve.GetBlock(blockCode); + if (newBlock == null) + { + continue; + } + + if (flipAxis != null) + { + if (flipAxis == EnumAxis.Y) + { + dy = SizeY - dy; + + AssetLocation newCode = newBlock.GetVerticallyFlippedBlockCode(); + newBlock = worldForResolve.GetBlock(newCode); + if (face.IsVertical) face = face.Opposite; + } + + if (flipAxis == EnumAxis.X) + { + dx = SizeX - dx; + + AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); + newBlock = worldForResolve.GetBlock(newCode); + if (face.Axis == EnumAxis.X) face = face.Opposite; + } + + if (flipAxis == EnumAxis.Z) + { + dz = SizeZ - dz; + + AssetLocation newCode = newBlock.GetHorizontallyFlippedBlockCode((EnumAxis)flipAxis); + newBlock = worldForResolve.GetBlock(newCode); + if (face.Axis == EnumAxis.Z) face = face.Opposite; + } + } + + if (angle != 0) + { + AssetLocation newCode = newBlock.GetRotatedBlockCode(angle); + newBlock = worldForResolve.GetBlock(newCode); + } + + BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); + + DecorsUnpacked.TryGetValue(pos, out Block[] decorsTmp); + if (decorsTmp == null) + { + decorsTmp = new Block[6]; + DecorsUnpacked[pos] = decorsTmp; + } + + decorsTmp[face.GetHorizontalRotated(angle).Index] = newBlock; + } + + + foreach (var val in BlockEntities) + { + uint index = val.Key; + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + if (flipAxis == EnumAxis.Y) dy = SizeY - dy; + if (flipAxis == EnumAxis.X) dx = SizeX - dx; + if (flipAxis == EnumAxis.Z) dz = SizeZ - dz; + BlockPos pos = getRotatedPos(aroundOrigin, angle, dx, dy, dz); + + string beData = val.Value; + + var block = worldForResolve.GetBlock(BlocksUnpacked[pos]); + string entityclass = block.EntityClass; + + if (entityclass != null) + { + BlockEntity be = worldForResolve.ClassRegistry.CreateBlockEntity(entityclass); + if (be is IRotatable rotatable) + { + be.Pos = pos; + be.CreateBehaviors(block, worldForResolve); + ITreeAttribute tree = DecodeBlockEntityData(beData); + rotatable.OnTransformed(worldForResolve ,tree, angle, BlockCodes, ItemCodes, flipAxis); + beData = StringEncodeTreeAttribute(tree); + } + + BlockEntitiesUnpacked[pos] = beData; + } + } + + + foreach (string entityData in Entities) + { + using (MemoryStream ms = new MemoryStream(Ascii85.Decode(entityData))) + { + BinaryReader reader = new BinaryReader(ms); + + string className = reader.ReadString(); + Entity entity = worldForResolve.ClassRegistry.CreateEntity(className); + + entity.FromBytes(reader, false); + + var pos = entity.ServerPos; + + double offx = 0; + double offz = 0; + + if (aroundOrigin != EnumOrigin.StartPos) + { + offx = SizeX / 2.0; + offz = SizeZ / 2.0; + } + + pos.X -= offx; + pos.Z -= offz; + + var x = pos.X; + var z = pos.Z; + + switch (angle) + { + case 90: + pos.X = -z + offz; // I have no idea why i need to add offz/offx flipped here for entities, but not for blocks (-‸ლ) + pos.Z = x + offx; + break; + case 180: + pos.X = -x + offx; + pos.Z = -z + offz; + break; + case 270: + pos.X = z + offz; + pos.Z = -x + offx; + break; + } + + pos.Yaw -= angle * GameMath.DEG2RAD; + entity.Pos.Yaw -= angle * GameMath.DEG2RAD; + + entity.Pos.SetPos(pos); + entity.PositionBeforeFalling.X = pos.X; + entity.PositionBeforeFalling.Z = pos.Z; + + entity.DidImportOrExport(startPos); + + + EntitiesUnpacked.Add(entity); + } + } + + Pack(worldForResolve, startPos); + } + + private BlockPos getRotatedPos(EnumOrigin aroundOrigin, int angle, int dx, int dy, int dz) + { + if (aroundOrigin != EnumOrigin.StartPos) + { + dx -= SizeX / 2; + dz -= SizeZ / 2; + } + + BlockPos pos = new BlockPos(dx, dy, dz); + + // 90 deg: + // xNew = -yOld + // yNew = xOld + + // 180 deg: + // xNew = -xOld + // yNew = -yOld + + // 270 deg: + // xNew = yOld + // yNew = -xOld + + switch (angle) + { + case 90: + pos.Set(-dz, dy, dx); + break; + case 180: + pos.Set(-dx, dy, -dz); + break; + case 270: + pos.Set(dz, dy, -dx); + break; + } + + if (aroundOrigin != EnumOrigin.StartPos) + { + pos.X += SizeX / 2; + pos.Z += SizeZ / 2; + } + + return pos; + } + + + /// + /// Places all the entities and blocks in the schematic at the position. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + public void PlaceEntitiesAndBlockEntities(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, Dictionary blockCodes, Dictionary itemCodes, bool replaceBlockEntities = false, Dictionary> replaceBlocks = null, int centerrockblockid = 0, Dictionary layerBlockForBlockEntities = null, bool resolveImports = true) + { + BlockPos curPos = startPos.Copy(); + + int schematicSeed = worldForCollectibleResolve.Rand.Next(); + + foreach (var val in BlockEntities) + { + uint index = val.Key; + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); + + BlockEntity be = blockAccessor.GetBlockEntity(curPos); + + // Block entities need to be manually initialized for world gen block access + if ((be == null || replaceBlockEntities) && blockAccessor is IWorldGenBlockAccessor) + { + Block block = blockAccessor.GetBlock(curPos, BlockLayersAccess.Solid); + + if (block.EntityClass != null) + { + blockAccessor.SpawnBlockEntity(block.EntityClass, curPos); + be = blockAccessor.GetBlockEntity(curPos); + } + } + + if (be != null) + { + if (!replaceBlockEntities) + { + Block block = blockAccessor.GetBlock(curPos, BlockLayersAccess.Solid); + if (block.EntityClass != worldForCollectibleResolve.ClassRegistry.GetBlockEntityClass(be.GetType())) + { + worldForCollectibleResolve.Logger.Warning("Could not import block entity data for schematic at {0}. There is already {1}, expected {2}. Probably overlapping ruins.", curPos, be.GetType(), block.EntityClass); + continue; + } + } + + ITreeAttribute tree = DecodeBlockEntityData(val.Value); + tree.SetInt("posx", curPos.X); + tree.SetInt("posy", curPos.InternalY); + tree.SetInt("posz", curPos.Z); + + be.FromTreeAttributes(tree, worldForCollectibleResolve); + be.OnLoadCollectibleMappings(worldForCollectibleResolve, blockCodes, itemCodes, schematicSeed, resolveImports); + Block layerBlock = null; + layerBlockForBlockEntities?.TryGetValue(curPos, out layerBlock); + be.OnPlacementBySchematic(worldForCollectibleResolve.Api as ICoreServerAPI, blockAccessor, curPos, replaceBlocks, centerrockblockid, layerBlock, resolveImports); + if (!(blockAccessor is IWorldGenBlockAccessor)) be.MarkDirty(); + } + } + + foreach (string entityData in Entities) + { + using (MemoryStream ms = new MemoryStream(Ascii85.Decode(entityData))) + { + BinaryReader reader = new BinaryReader(ms); + + string className = reader.ReadString(); + Entity entity = worldForCollectibleResolve.ClassRegistry.CreateEntity(className); + + entity.FromBytes(reader, false, ((IServerWorldAccessor)worldForCollectibleResolve).RemappedEntities); + entity.DidImportOrExport(startPos); + + if (worldForCollectibleResolve.GetEntityType(entity.Code) != null) // Can be null if its a no longer existent mob type + { + // Not ideal but whatever + if (blockAccessor is IWorldGenBlockAccessor accessor) + { + accessor.AddEntity(entity); + entity.OnInitialized += () => entity.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes, schematicSeed, resolveImports); + } else + { + worldForCollectibleResolve.SpawnEntity(entity); + if (blockAccessor is IBlockAccessorRevertable re) + { + re.StoreEntitySpawnToHistory(entity); + } + entity.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes, schematicSeed, resolveImports); + } + } + else + { + worldForCollectibleResolve.Logger.Error("Couldn't import entity {0} with id {1} and code {2} - it's Type is null! Maybe from an older game version or a missing mod.", entity.GetType(), entity.EntityId, entity.Code); + } + } + } + } + + /// + /// Gets just the positions of the blocks. + /// + /// The origin point to start from + /// An array containing the BlockPos of each block in the area. + public virtual BlockPos[] GetJustPositions(BlockPos origin) + { + BlockPos[] positions = new BlockPos[Indices.Count]; + + for (int i = 0; i < Indices.Count; i++) + { + uint index = Indices[i]; + + int dx = (int)(index & 0x1ff); + int dy = (int)((index >> 20) & 0x1ff); + int dz = (int)((index >> 10) & 0x1ff); + + BlockPos pos = new BlockPos(dx, dy, dz); + positions[i] = pos.Add(origin); + } + + return positions; + } + + + /// + /// Gets the starting position of the schematic. + /// + /// + /// + /// + public virtual BlockPos GetStartPos(BlockPos pos, EnumOrigin origin) + { + return AdjustStartPos(pos.Copy(), origin); + } + + /// + /// Adjusts the starting position of the schemtic. + /// + /// + /// + /// + public virtual BlockPos AdjustStartPos(BlockPos startpos, EnumOrigin origin) + { + if (origin == EnumOrigin.TopCenter) + { + startpos.X -= SizeX / 2; + startpos.Y -= SizeY; + startpos.Z -= SizeZ / 2; + } + + if (origin == EnumOrigin.BottomCenter) + { + startpos.X -= SizeX / 2; + startpos.Z -= SizeZ / 2; + } + + if (origin == EnumOrigin.MiddleCenter) + { + startpos.X -= SizeX / 2; + startpos.Y -= SizeY / 2; + startpos.Z -= SizeZ / 2; + } + + return startpos; + } + + /// + /// Loads the schematic from a file. + /// + /// + /// + /// + public static BlockSchematic LoadFromFile(string infilepath, ref string error) + { + if (!File.Exists(infilepath) && File.Exists(infilepath + ".json")) + { + infilepath += ".json"; + } + + if (!File.Exists(infilepath)) + { + error = "Can't import " + infilepath + ", it does not exist"; + return null; + } + + BlockSchematic blockdata = null; + + try + { + using (TextReader textReader = new StreamReader(infilepath)) + { + blockdata = JsonConvert.DeserializeObject(textReader.ReadToEnd()); + textReader.Close(); + } + } + catch (Exception e) + { + error = "Failed loading " + infilepath + " : " + e.Message; + return null; + } + + return blockdata; + } + + /// + /// Loads a schematic from a string. + /// + /// + /// + /// + public static BlockSchematic LoadFromString(string jsoncode, ref string error) + { + try + { + return JsonConvert.DeserializeObject(jsoncode); + } + catch (Exception e) + { + error = "Failed loading schematic from json code : " + e.Message; + return null; + } + } + + /// + /// Saves a schematic to a file. + /// + /// + /// + public virtual string Save(string outfilepath) + { + this.GameVersion = API.Config.GameVersion.ShortGameVersion; + + if (!outfilepath.EndsWith(".json")) + { + outfilepath += ".json"; + } + + try + { + using (TextWriter textWriter = new StreamWriter(outfilepath)) + { + textWriter.Write(JsonConvert.SerializeObject(this, Formatting.None)); + textWriter.Close(); + } + } + catch (IOException e) + { + return "Failed exporting: " + e.Message; + } + + return null; + } + + + public virtual string ToJson() + { + this.GameVersion = API.Config.GameVersion.ShortGameVersion; + return JsonConvert.SerializeObject(this, Formatting.None); + } + + + + /// + /// Exports the block entity data to a string. + /// + /// + /// + public virtual string EncodeBlockEntityData(BlockEntity be) + { + TreeAttribute tree = new TreeAttribute(); + be.ToTreeAttributes(tree); + + return StringEncodeTreeAttribute(tree); + } + + /// + /// Exports the tree attribute data to a string. + /// + /// + /// + public virtual string StringEncodeTreeAttribute(ITreeAttribute tree) + { + byte[] data; + using (MemoryStream ms = new MemoryStream()) + { + BinaryWriter writer = new BinaryWriter(ms); + tree.ToBytes(writer); + data = ms.ToArray(); + } + + return Ascii85.Encode(data); + } + + + /// + /// Imports the tree data from a string. + /// + /// + /// + public virtual TreeAttribute DecodeBlockEntityData(string data) + { + byte[] bedata = Ascii85.Decode(data); + + TreeAttribute tree = new TreeAttribute(); + + using (MemoryStream ms = new MemoryStream(bedata)) + { + BinaryReader reader = new BinaryReader(ms); + tree.FromBytes(reader); + } + + return tree; + } + + + + + + protected virtual int PlaceReplaceAll(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) + { + blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); + return 1; + } + + protected virtual int PlaceReplaceable(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) + { + if (newBlock.ForFluidsLayer || blockAccessor.GetBlock(pos, BlockLayersAccess.MostSolid).Replaceable > newBlock.Replaceable) + { + blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); + return 1; + } + return 0; + } + + protected virtual int PlaceReplaceAllNoAir(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) + { + if (newBlock.BlockId != 0) + { + blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); + return 1; + } + return 0; + } + + protected virtual int PlaceReplaceOnlyAir(IBlockAccessor blockAccessor, BlockPos pos, Block newBlock, bool replaceMeta) + { + Block oldBlock = blockAccessor.GetMostSolidBlock(pos); + if (oldBlock.BlockId == 0) + { + blockAccessor.SetBlock(replaceMeta && (newBlock == fillerBlock || newBlock == pathwayBlock) ? empty : newBlock.BlockId, pos); + return 1; + } + return 0; + } + + + + /// + /// Makes a deep copy of the packed schematic. Unpacked data and loaded meta information is not cloned. + /// + /// + public virtual BlockSchematic ClonePacked() + { + BlockSchematic cloned = new BlockSchematic(); + cloned.SizeX = SizeX; + cloned.SizeY = SizeY; + cloned.SizeZ = SizeZ; + cloned.GameVersion = GameVersion; + cloned.BlockCodes = new Dictionary(BlockCodes); + cloned.ItemCodes = new Dictionary(ItemCodes); + cloned.Indices = new List(Indices); + cloned.BlockIds = new List(BlockIds); + cloned.BlockEntities = new Dictionary(BlockEntities); + cloned.Entities = new List(Entities); + cloned.ReplaceMode = ReplaceMode; + cloned.EntranceRotation = EntranceRotation; + cloned.DecorIndices = new List(DecorIndices); + cloned.DecorIds = new List(DecorIds); + return cloned; + } + } } \ No newline at end of file diff --git a/Common/Collectible/Block/IRotatable.cs b/Common/Collectible/Block/IRotatable.cs index 393c069a..286c7111 100644 --- a/Common/Collectible/Block/IRotatable.cs +++ b/Common/Collectible/Block/IRotatable.cs @@ -1,4 +1,5 @@ -using Vintagestory.API.Datastructures; +using System.Collections.Generic; +using Vintagestory.API.Datastructures; using Vintagestory.API.MathTools; namespace Vintagestory.API.Common @@ -12,10 +13,15 @@ public interface IRotatable /// /// If flipAxis is null it means it was not flipped, only horizontally rotated. Apply flip first, and then rotation. /// + /// /// /// + /// Used for rotation of schematics, so microblocks can update their materials + /// /// - void OnTransformed(ITreeAttribute tree, int degreeRotation, EnumAxis? flipAxis); + void OnTransformed(IWorldAccessor worldAccessor, ITreeAttribute tree, int degreeRotation, + Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, + EnumAxis? flipAxis); } public interface IMaterialExchangeable diff --git a/Common/Collectible/Block/VertexFlags.cs b/Common/Collectible/Block/VertexFlags.cs index f6d21597..e1cd0433 100644 --- a/Common/Collectible/Block/VertexFlags.cs +++ b/Common/Collectible/Block/VertexFlags.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using System; using Vintagestory.API.MathTools; namespace Vintagestory.API.Common @@ -70,6 +71,9 @@ public enum EnumWindBitMode WaterPlant = 11 } + /// + /// Windmode flags, which can be ORed with existing vertex data to add the specified wind mode (assuming it was 0 previously!) + /// public static class EnumWindBitModeMask { /// @@ -114,7 +118,7 @@ public static class EnumWindBitModeMask /// Bit 12: Lod 0 Bit
/// Bit 13-24: X/Y/Z Normals
/// Bit 25, 26, 27, 28: Wind mode
- /// Bit 29, 30, 31: Wind data
+ /// Bit 29, 30, 31: Wind data (also sometimes used for other data, e.g. reflection mode if Reflective bit is set, or additional water surface data if this is a water block)
///
[JsonObject(MemberSerialization.OptIn)] public class VertexFlags @@ -122,7 +126,7 @@ public class VertexFlags /// /// Bit 0..7 /// - public const int GlowLevelBitMask = 0xFF; + public const int GlowLevelBitMask = 0xFF; // VS 1.19 note: in future if we ever needed more bits, we can find 7 bits here if we change glow level to a 7-bit value with bit 0 signifying glow on/off : with glow off, the 7 bits can be used for some other kind of data public const int ZOffsetBitPos = 8; /// @@ -131,7 +135,7 @@ public class VertexFlags public const int ZOffsetBitMask = 0x7 << ZOffsetBitPos; /// - /// Bit 11 + /// Bit 11. Note if this is set to 1, then WindData has a different meaning, /// public const int ReflectiveBitMask = 1 << 11; /// @@ -141,21 +145,22 @@ public class VertexFlags public const int NormalBitPos = 13; /// - /// Bit 13..25 + /// Bit 13..24 /// public const int NormalBitMask = 0xFFF << NormalBitPos; /// /// Bit 25..28 /// - public const int WindModeBitsMask = 0xF << 25; + public const int WindModeBitsMask = 0xF << WindModeBitsPos; public const int WindModeBitsPos = 25; /// - /// Bit 29..31 + /// Bit 29..31 Note that WindData is sometimes used for other purposes if WindMode == 0, for example it can hold reflections data, see EnumReflectiveMode. + ///
Also worth noting that WindMode and WindData have totally different meanings for liquid water ///
- public const int WindDataBitsMask = 0x7 << 29; + public const int WindDataBitsMask = 0x7 << WindDataBitsPos; public const int WindDataBitsPos = 29; @@ -455,6 +460,21 @@ public override string ToString() glowLevel, ZOffset, reflective, lod0, normal, WindMode, windData ); } + + public static void SetWindMode(ref int flags, int windMode) + { + flags |= windMode << VertexFlags.WindModeBitsPos; + } + + public static void SetWindData(ref int flags, int windData) + { + flags |= windData << VertexFlags.WindDataBitsPos; + } + + public static void ReplaceWindData(ref int flags, int windData) + { + flags = flags & VertexFlags.ClearWindDataBitsMask | windData << VertexFlags.WindDataBitsPos; + } } } diff --git a/Common/Collectible/Collectible.cs b/Common/Collectible/Collectible.cs index 8d65d358..323db76d 100644 --- a/Common/Collectible/Collectible.cs +++ b/Common/Collectible/Collectible.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics.Metrics; using System.Linq; -using System.Numerics; using System.Text; using Vintagestory.API.Client; using Vintagestory.API.Common.Entities; @@ -554,6 +552,7 @@ public virtual void OnModifiedInInventorySlot(IWorldAccessor world, ItemSlot slo /// /// /// + /// /// public virtual bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, ItemSlot itemslot, BlockSelection blockSel, float dropQuantityMultiplier = 1) { @@ -591,12 +590,13 @@ public virtual bool OnBlockBrokenWith(IWorldAccessor world, Entity byEntity, Ite } - /// /// Called every game tick when the player breaks a block with this item in his hands. Returns the mining speed for given block. /// /// + /// /// + /// /// public virtual float GetMiningSpeed(IItemStack itemstack, BlockSelection blockSel, Block block, IPlayer forPlayer) { @@ -641,6 +641,7 @@ public virtual string GetHeldTpHitAnimation(ItemSlot slot, Entity byEntity) ///
/// /// + /// /// public virtual string GetHeldReadyAnimation(ItemSlot activeHotbarSlot, Entity forEntity, EnumHand hand) { @@ -653,6 +654,7 @@ public virtual string GetHeldReadyAnimation(ItemSlot activeHotbarSlot, Entity fo ///
/// /// + /// /// public virtual string GetHeldTpIdleAnimation(ItemSlot activeHotbarSlot, Entity forEntity, EnumHand hand) { @@ -1021,6 +1023,7 @@ public virtual void InGuiIdle(IWorldAccessor world, ItemStack stack) /// /// Called when this item was collected by an entity /// + /// /// public virtual void OnCollected(ItemStack stack, Entity entity) { @@ -1041,7 +1044,7 @@ public virtual void OnCollected(ItemStack stack, Entity entity) /// True on first mouse down /// Whether or not to do any subsequent actions. If not set or set to NotHandled, the action will not called on the server. /// - public void OnHeldUseStart(ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel, EnumHandInteract useType, bool firstEvent, ref EnumHandHandling handling) + public virtual void OnHeldUseStart(ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel, EnumHandInteract useType, bool firstEvent, ref EnumHandHandling handling) { if (useType == EnumHandInteract.HeldItemAttack) { @@ -1052,9 +1055,7 @@ public void OnHeldUseStart(ItemSlot slot, EntityAgent byEntity, BlockSelection b if (useType == EnumHandInteract.HeldItemInteract) { OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling); - return; } - } /// @@ -1216,22 +1217,26 @@ public virtual void OnHeldInteractStart(ItemSlot slot, EntityAgent byEntity, Blo { EnumHandHandling bhHandHandling = EnumHandHandling.NotHandled; bool preventDefault = false; - + foreach (CollectibleBehavior behavior in CollectibleBehaviors) { EnumHandling bhHandling = EnumHandling.PassThrough; - + behavior.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref bhHandHandling, ref bhHandling); if (bhHandling != EnumHandling.PassThrough) { handling = bhHandHandling; preventDefault = true; } - + if (bhHandling == EnumHandling.PreventSubsequent) return; } - if (!preventDefault) tryEatBegin(slot, byEntity, ref bhHandHandling); + if (!preventDefault) + { + tryEatBegin(slot, byEntity, ref bhHandHandling); + handling = bhHandHandling; + } } @@ -1333,13 +1338,14 @@ public virtual bool OnHeldInteractCancel(float secondsUsed, ItemSlot slot, Entit } - /// /// Tries to eat the contents in the slot, first call /// /// /// /// + /// + /// protected virtual void tryEatBegin(ItemSlot slot, EntityAgent byEntity, ref EnumHandHandling handling, string eatSound = "eat", int eatSoundRepeats = 1) { if (!slot.Empty && GetNutritionProperties(byEntity.World, slot.Itemstack, byEntity) != null) @@ -1375,6 +1381,7 @@ protected void playEatSound(EntityAgent byEntity, string eatSound = "eat", int e /// /// /// + /// protected virtual bool tryEatStep(float secondsUsed, ItemSlot slot, EntityAgent byEntity, ItemStack spawnParticleStack = null) { if (GetNutritionProperties(byEntity.World, slot.Itemstack, byEntity) == null) return false; @@ -1924,6 +1931,36 @@ protected virtual float AppendPerishableInfoText(ItemSlot inSlot, StringBuilder } } break; + + case EnumTransitionType.Melt: + if (nowSpoiling) break; + + if (transitionLevel > 0 || freshHoursLeft <= 0) + { + dsc.AppendLine(Lang.Get("itemstack-meltable-melted", (int)Math.Round(transitionLevel * 100))); + dsc.AppendLine(Lang.Get("Melting rate in this container: {0:0.##}x", transitionRate)); + } + else + { + double hoursPerday = api.World.Calendar.HoursPerDay; + + if (transitionRate <= 0) + { + dsc.AppendLine(Lang.Get("itemstack-meltable")); + } + else + { + if (freshHoursLeft > hoursPerday) + { + dsc.AppendLine(Lang.Get("itemstack-meltable-duration-days", Math.Round(freshHoursLeft / hoursPerday, 1))); + } + else + { + dsc.AppendLine(Lang.Get("itemstack-meltable-duration-hours", Math.Round(freshHoursLeft, 1))); + } + } + } + break; } return 0; @@ -2004,6 +2041,7 @@ public virtual bool CanBePlacedInto(ItemStack stack, ItemSlot slot) /// /// /// + /// /// public virtual int GetMergableQuantity(ItemStack sinkStack, ItemStack sourceStack, EnumMergePriority priority) { @@ -2633,12 +2671,7 @@ public virtual bool HasTemperature(IItemStack itemstack) /// public virtual float GetTemperature(IWorldAccessor world, ItemStack itemstack) { - if ( - itemstack == null - || itemstack.Attributes == null - || itemstack.Attributes["temperature"] == null - || !(itemstack.Attributes["temperature"] is ITreeAttribute) - ) + if (itemstack?.Attributes?["temperature"] is not ITreeAttribute) { return 20; } @@ -2688,10 +2721,6 @@ public virtual void SetTemperature(IWorldAccessor world, ItemStack itemstack, fl attr.SetFloat("temperature", temperature); } - - - - /// /// Returns true if this stack is an empty backpack /// @@ -2786,8 +2815,13 @@ public virtual void OnStoreCollectibleMappings(IWorldAccessor world, ItemSlot in } OnStoreCollectibleMappings(world, inSlot.Itemstack.Attributes, blockIdMapping, itemIdMapping); + + // on export of schematic save the temperature to the TreeAttribute since in import we need the temperatureLastUpdate to be up to date + if ((inSlot.Itemstack.Attributes["temperature"] as ITreeAttribute)?.HasAttribute("temperatureLastUpdate") == true) + { + GetTemperature(world, inSlot.Itemstack); + } } - /// /// This method is called after a block/item like this has been imported as part of a block schematic. Has to restore fix the block/item id mappings as they are probably different compared to the world from where they were exported. By default iterates over all the itemstacks attributes and searches for attribute sof type ItenStackAttribute and calls .FixMapping() on them. /// @@ -2795,7 +2829,21 @@ public virtual void OnStoreCollectibleMappings(IWorldAccessor world, ItemSlot in /// /// /// + [Obsolete("Use the variant with resolveImports parameter")] public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForResolve, ItemSlot inSlot, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping) + { + OnLoadCollectibleMappings(worldForResolve, inSlot, oldBlockIdMapping, oldItemIdMapping, true); + } + + /// + /// This method is called after a block/item like this has been imported as part of a block schematic. Has to restore fix the block/item id mappings as they are probably different compared to the world from where they were exported. By default iterates over all the itemstacks attributes and searches for attribute sof type ItenStackAttribute and calls .FixMapping() on them. + /// + /// + /// + /// + /// + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForResolve, ItemSlot inSlot, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, bool resolveImports) { OnLoadCollectibleMappings(worldForResolve, inSlot.Itemstack.Attributes, oldBlockIdMapping, oldItemIdMapping); } @@ -2804,47 +2852,63 @@ private void OnLoadCollectibleMappings(IWorldAccessor worldForResolve, ITreeAttr { foreach (var val in tree) { - if (val.Value is ITreeAttribute) + if (val.Value is ITreeAttribute treeAttribute) { - OnLoadCollectibleMappings(worldForResolve, val.Value as ITreeAttribute, oldBlockIdMapping, oldItemIdMapping); + OnLoadCollectibleMappings(worldForResolve, treeAttribute, oldBlockIdMapping, oldItemIdMapping); continue; } - if (val.Value is ItemstackAttribute) + if (val.Value is ItemstackAttribute itemAttribute) { - ItemStack stack = (val.Value as ItemstackAttribute).value; + ItemStack stack = itemAttribute.value; stack?.FixMapping(oldBlockIdMapping, oldItemIdMapping, worldForResolve); + stack?.Collectible.OnLoadCollectibleMappings(worldForResolve, stack.Attributes, oldBlockIdMapping, oldItemIdMapping); } } + // update the time for the temperature to the current ingame time if imported from another game + if (tree.HasAttribute("temperatureLastUpdate")) + { + tree.SetDouble("temperatureLastUpdate", worldForResolve.Calendar.TotalHours); + } + + // update food transition time + if (tree.HasAttribute("createdTotalHours")) + { + var created = tree.GetDouble("createdTotalHours"); + var lasUpdated = tree.GetDouble("lastUpdatedTotalHours"); + var diff = lasUpdated - created; + tree.SetDouble("lastUpdatedTotalHours", worldForResolve.Calendar.TotalHours); + tree.SetDouble("createdTotalHours", worldForResolve.Calendar.TotalHours - diff); + } + } void OnStoreCollectibleMappings(IWorldAccessor world, ITreeAttribute tree, Dictionary blockIdMapping, Dictionary itemIdMapping) { foreach (var val in tree) { - if (val.Value is ITreeAttribute) + if (val.Value is ITreeAttribute treeAttribute) { - OnStoreCollectibleMappings(world, val.Value as ITreeAttribute, blockIdMapping, itemIdMapping); + OnStoreCollectibleMappings(world, treeAttribute, blockIdMapping, itemIdMapping); continue; } - if (val.Value is ItemstackAttribute) + if (val.Value is ItemstackAttribute attribute) { - ItemStack stack = (val.Value as ItemstackAttribute).value; + ItemStack stack = attribute.value; if (stack == null) continue; if (stack.Collectible == null) stack.ResolveBlockOrItem(world); if (stack.Class == EnumItemClass.Item) { - itemIdMapping[stack.Id] = stack.Collectible.Code; + itemIdMapping[stack.Id] = stack.Collectible?.Code; } else { - blockIdMapping[stack.Id] = stack.Collectible.Code; + blockIdMapping[stack.Id] = stack.Collectible?.Code; } } } - } diff --git a/Common/Collectible/CollectibleBehavior.cs b/Common/Collectible/CollectibleBehavior.cs index 9feb2d77..9a428100 100644 --- a/Common/Collectible/CollectibleBehavior.cs +++ b/Common/Collectible/CollectibleBehavior.cs @@ -121,7 +121,6 @@ public virtual void OnHeldAttackStop(float secondsPassed, ItemSlot slot, EntityA } - /// /// Called when the player right clicks while holding this block/item in his hands /// @@ -129,6 +128,7 @@ public virtual void OnHeldAttackStop(float secondsPassed, ItemSlot slot, EntityA /// /// /// + /// /// Whether or not to do any subsequent actions. If not set or set to NotHandled, the action will not called on the server. /// Set to PreventDefault to not try eating the item, set to PreventSubsequent to ignore any subsequent calls to OnHeldInteractStart() of other behaviors /// diff --git a/Common/Collectible/IItemStack.cs b/Common/Collectible/IItemStack.cs index 311e3751..5e421e22 100644 --- a/Common/Collectible/IItemStack.cs +++ b/Common/Collectible/IItemStack.cs @@ -46,7 +46,9 @@ public interface IItemStack /// /// Checks if this item stack is of the same class, id and has the same stack attributes. Ignores stack size /// + /// /// + /// /// bool Equals(IWorldAccessor worldForResolve, ItemStack sourceStack, params string[] ignoreAttributeSubTrees); @@ -65,6 +67,7 @@ public interface IItemStack /// /// Checks if the contained item or block name contains given searchtext /// + /// /// /// bool MatchesSearchText(IWorldAccessor world, string searchText); @@ -79,7 +82,7 @@ public interface IItemStack /// Returns a multiline description text of the item /// /// - /// + /// /// Whether to show additional debug info /// string GetDescription(IWorldAccessor world, ItemSlot inSlot, bool debug = false); diff --git a/Common/Collectible/IResolvableCollectible.cs b/Common/Collectible/IResolvableCollectible.cs index 37142c56..5fb806cc 100644 --- a/Common/Collectible/IResolvableCollectible.cs +++ b/Common/Collectible/IResolvableCollectible.cs @@ -1,13 +1,10 @@ -using System.IO; -using Vintagestory.API.Datastructures; - -namespace Vintagestory.API.Common +namespace Vintagestory.API.Common { /// /// /// public interface IResolvableCollectible { - void Resolve(ItemSlot intoslot, IWorldAccessor worldForResolve); + void Resolve(ItemSlot intoslot, IWorldAccessor worldForResolve, bool resolveImports = true); } } diff --git a/Common/Collectible/ItemStack.cs b/Common/Collectible/ItemStack.cs index 1ea0d1dc..15689a59 100644 --- a/Common/Collectible/ItemStack.cs +++ b/Common/Collectible/ItemStack.cs @@ -391,6 +391,7 @@ public string GetName() /// Returns a human readable description of the item/block /// /// + /// /// /// public string GetDescription(IWorldAccessor world, ItemSlot inSlot, bool debug = false) diff --git a/Common/Collectible/JsonItemStack.cs b/Common/Collectible/JsonItemStack.cs index d6e53b20..88b217e4 100644 --- a/Common/Collectible/JsonItemStack.cs +++ b/Common/Collectible/JsonItemStack.cs @@ -55,6 +55,9 @@ public static JsonItemStack FromString(string jsonItemstack) /// Sets itemstack.block or itemstack.item /// /// + /// + /// + /// public bool Resolve(IWorldAccessor resolver, string sourceForErrorLogging, AssetLocation assetLoc, bool printWarningOnError = true) { if (Type == EnumItemClass.Block) diff --git a/Common/Collectible/TransitionableProperties.cs b/Common/Collectible/TransitionableProperties.cs index a5b17d20..6ffc9871 100644 --- a/Common/Collectible/TransitionableProperties.cs +++ b/Common/Collectible/TransitionableProperties.cs @@ -26,7 +26,12 @@ public enum EnumTransitionType /// /// Cheese ripening /// - Ripen + Ripen, + + /// + /// Snow/ice melting + /// + Melt } diff --git a/Common/Crafting/CraftingRecipeIngredient.cs b/Common/Crafting/CraftingRecipeIngredient.cs index 5c5f6a28..2cd7d27f 100644 --- a/Common/Crafting/CraftingRecipeIngredient.cs +++ b/Common/Crafting/CraftingRecipeIngredient.cs @@ -80,6 +80,7 @@ public class CraftingRecipeIngredient : IRecipeIngredient /// Turns Type, Code and Attributes into an IItemStack /// /// + /// public bool Resolve(IWorldAccessor resolver, string sourceForErrorLogging) { if (ReturnedStack != null) @@ -131,6 +132,7 @@ public bool Resolve(IWorldAccessor resolver, string sourceForErrorLogging) /// Checks whether or not the input satisfies as an ingredient for the recipe. /// /// + /// /// public bool SatisfiesAsIngredient(ItemStack inputStack, bool checkStacksize = true) { diff --git a/Common/Crafting/GridRecipe.cs b/Common/Crafting/GridRecipe.cs index e3897c61..ba3a53d2 100644 --- a/Common/Crafting/GridRecipe.cs +++ b/Common/Crafting/GridRecipe.cs @@ -546,7 +546,6 @@ public bool MatchesAtPosition(int colStart, int rowStart, ItemSlot[] inputSlots, /// /// /// - /// /// public ItemStack GetInputStackForPatternCode(string patternCode, ItemSlot[] inputSlots) { diff --git a/Common/Crafting/LayeredVoxelRecipe.cs b/Common/Crafting/LayeredVoxelRecipe.cs index a270e378..d2c3fb19 100644 --- a/Common/Crafting/LayeredVoxelRecipe.cs +++ b/Common/Crafting/LayeredVoxelRecipe.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text.RegularExpressions; using Vintagestory.API.Util; diff --git a/Common/Entity/Entity.cs b/Common/Entity/Entity.cs index 5d9ca6ef..63fdf635 100644 --- a/Common/Entity/Entity.cs +++ b/Common/Entity/Entity.cs @@ -126,7 +126,7 @@ static Entity() /// Server simulated animations. Only takes care of stopping animations once they're done /// Set and Called by the Entities ServerSystem /// - public IAnimationManager AnimManager; + public virtual IAnimationManager AnimManager { get; set; } /// /// An uptime value running activities. Available on the game client and server. Not synchronized. @@ -302,6 +302,10 @@ public bool IsOnFire /// public object packet; + /// + /// Used only when deserialising an entity, otherwise null + /// + private Dictionary codeRemaps; #endregion #region Properties @@ -444,7 +448,7 @@ public int RenderColor /// public Entity() { - SimulationRange = GlobalConstants.DefaultTrackingRange; + SimulationRange = GlobalConstants.DefaultSimulationRange; AnimManager = new AnimationManager(); Stats = new EntityStats(this); WatchedAttributes.SetAttribute("animations", new TreeAttribute()); @@ -720,6 +724,7 @@ public virtual ItemStack[] GetDrops(IWorldAccessor world, BlockPos pos, IPlayer /// /// /// + /// public virtual void TeleportToDouble(double x, double y, double z, Action onTeleported = null) { Teleporting = true; @@ -727,7 +732,7 @@ public virtual void TeleportToDouble(double x, double y, double z, Action onTele ICoreServerAPI sapi = this.World.Api as ICoreServerAPI; if (sapi != null) { - sapi.WorldManager.LoadChunkColumnPriority((int)ServerPos.X / World.BlockAccessor.ChunkSize, (int)ServerPos.Z / World.BlockAccessor.ChunkSize, new ChunkLoadOptions() { OnLoaded = () => + sapi.WorldManager.LoadChunkColumnPriority((int)ServerPos.X / GlobalConstants.ChunkSize, (int)ServerPos.Z / GlobalConstants.ChunkSize, new ChunkLoadOptions() { OnLoaded = () => { IsTeleport = true; Pos.SetPos(x, y, z); @@ -775,6 +780,7 @@ public virtual void TeleportTo(BlockPos position) /// Teleports the entity to given position /// /// + /// public virtual void TeleportTo(EntityPos position, Action onTeleported = null) { Pos.Yaw = position.Yaw; @@ -1483,7 +1489,18 @@ public virtual void UpdateDebugAttributes() } - + /// + /// In order to maintain legacy mod API compatibility of FromBytes(BinaryReader reader, bool isSync), we create an overload which server-side calling code will actually call, and store the remaps parameter in a field + /// + /// + /// + /// + public virtual void FromBytes(BinaryReader reader, bool isSync, Dictionary serversideRemaps) + { + this.codeRemaps = serversideRemaps; + this.FromBytes(reader, isSync); + this.codeRemaps = null; + } /// /// Loads the entity from a stored byte array from the SaveGame @@ -1524,7 +1541,10 @@ public virtual void FromBytes(BinaryReader reader, bool isSync) PositionBeforeFalling.X = reader.ReadDouble(); PositionBeforeFalling.Y = reader.ReadDouble(); PositionBeforeFalling.Z = reader.ReadDouble(); - Code = new AssetLocation(reader.ReadString()); + + string codeString = reader.ReadString(); + if (codeRemaps != null && codeRemaps.TryGetValue(codeString, out string remappedString)) codeString = remappedString; + Code = new AssetLocation(codeString); if (!isSync) { @@ -1834,14 +1854,31 @@ public virtual void OnStoreCollectibleMappings(Dictionary bl /// Note: Some vanilla blocks resolve randomized contents in this method. /// Hint: Use itemstack.FixMapping() to do the job for you. /// + /// /// /// /// If you need some sort of randomness consistency accross an imported schematic, you can use this value + [Obsolete("Use the variant with resolveImports parameter")] public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed) + { + OnLoadCollectibleMappings(worldForNewMappings, oldItemIdMapping, oldItemIdMapping, schematicSeed, true); + } + + /// + /// Called by the blockschematic loader so that you may fix any blockid/itemid mappings against the mapping of the savegame, if you store any collectibles in this blockentity. + /// Note: Some vanilla blocks resolve randomized contents in this method. + /// Hint: Use itemstack.FixMapping() to do the job for you. + /// + /// + /// + /// + /// If you need some sort of randomness consistency accross an imported schematic, you can use this value + /// Turn it off to spawn structures as they are. For example, in this mode, instead of traders, their meta spawners will spawn + public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed, bool resolveImports) { foreach (var val in Properties.Server.Behaviors) { - val.OnLoadCollectibleMappings(worldForNewMappings, oldBlockIdMapping, oldItemIdMapping); + val.OnLoadCollectibleMappings(worldForNewMappings, oldBlockIdMapping, oldItemIdMapping, resolveImports); } } @@ -1900,6 +1937,13 @@ public virtual string GetInfoText() } } + var capi = Api as ICoreClientAPI; + if (capi != null && capi.Settings.Bool["extendedDebugInfo"]) + { + infotext.AppendLine("Id:" + EntityId + ""); + infotext.AppendLine("Code: " + Code + ""); + } + return infotext.ToString(); } diff --git a/Common/Entity/EntityAgent.cs b/Common/Entity/EntityAgent.cs index 451c8c47..f65c9c9e 100644 --- a/Common/Entity/EntityAgent.cs +++ b/Common/Entity/EntityAgent.cs @@ -23,7 +23,7 @@ public interface IWearableShapeSupplier /// /// /// null for returning back to default behavior (read shape from attributes) - Shape GetShape(ItemStack stack, EntityAgent forEntity); + Shape GetShape(ItemStack stack, EntityAgent forEntity, string texturePrefixCode); } @@ -391,6 +391,7 @@ public override bool ReceiveDamage(DamageSource damageSource, float damage) /// The amount of saturation recieved. /// The cat of food... err Category of food. /// The delay before the loss of saturation + /// public virtual void ReceiveSaturation(float saturation, EnumFoodCategory foodCat = EnumFoodCategory.Unknown, float saturationLossDelay = 10, float nutritionGainMultiplier = 1f) { if (!Alive) return; @@ -410,6 +411,7 @@ public virtual void ReceiveSaturation(float saturation, EnumFoodCategory foodCat /// The amount of saturation recieved. /// The cat of food... err Category of food. /// The delay before the loss of saturation + /// public virtual bool ShouldReceiveSaturation(float saturation, EnumFoodCategory foodCat = EnumFoodCategory.Unknown, float saturationLossDelay = 10, float nutritionGainMultiplier = 1f) { return true; @@ -497,6 +499,7 @@ public override void OnGameTick(float dt) } } + if (!anyAverageAnimActive && defaultAnim != null && Alive && !skipDefaultAnim) { defaultAnim.WasStartedFromTrigger = true; @@ -508,7 +511,6 @@ public override void OnGameTick(float dt) AnimManager.StopAnimation(defaultAnim.Code); } - bool isSelf = (Api as ICoreClientAPI).World.Player.Entity.EntityId == EntityId; if (insideBlock?.GetBlockMaterial(Api.World.BlockAccessor, insidePos) == EnumBlockMaterial.Snow && isSelf) { @@ -778,7 +780,7 @@ protected Shape addGearToShape(ref Shape entityShape, string shapePathForLogging foreach (var slot in inv) { - if (slot.Empty) continue; + if (slot.Empty) continue; if (hideClothing && !slot.Empty) { continue; @@ -827,12 +829,15 @@ protected virtual Shape addGearToShape(ItemSlot slot, Shape entityShape, string if (attrObj?["wearableAttachment"].Exists != true) return entityShape; - Shape gearShape=null; + var textures = Properties.Client.Textures; + string texturePrefixCode = stack.Collectible.Code.ToShortString(); + + Shape gearShape = null; AssetLocation shapePath; CompositeShape compGearShape = null; if (stack.Collectible is IWearableShapeSupplier iwss) { - gearShape = iwss.GetShape(stack, this); + gearShape = iwss.GetShape(stack, this, texturePrefixCode); } if (gearShape == null) { @@ -844,9 +849,11 @@ protected virtual Shape addGearToShape(ItemSlot slot, Shape entityShape, string Api.World.Logger.Warning("Entity armor shape {0} defined in {1} {2} not found or errored, was supposed to be at {3}. Armor piece will be invisible.", compGearShape.Base, stack.Class, stack.Collectible.Code, shapePath); return null; } + + gearShape.SubclassForStepParenting(texturePrefixCode, damageEffect); } - string texturePrefixCode = stack.Collectible.Code.ToShortString(); + // Item stack textures take precedence over shape textures if (gearShape.Textures == null) gearShape.Textures = new Dictionary(); @@ -856,11 +863,9 @@ protected virtual Shape addGearToShape(ItemSlot slot, Shape entityShape, string gearShape.Textures[val.Key] = val.Value.Base; } - var textures = Properties.Client.Textures; entityShape.StepParentShape( gearShape, - texturePrefixCode, - compGearShape.Base.ToString() + string.Format("defined in {0} {1}", stack.Class, stack.Collectible.Code), + (compGearShape?.Base.ToString() ?? "Custom texture from ItemWearableShapeSupplier ") + string.Format("defined in {0} {1}", stack.Class, stack.Collectible.Code), shapePathForLogging, Api.World.Logger, (texcode, tloc) => @@ -869,8 +874,7 @@ protected virtual Shape addGearToShape(ItemSlot slot, Shape entityShape, string cmpt.Bake(Api.Assets); (Api as ICoreClientAPI).EntityTextureAtlas.GetOrInsertTexture(cmpt.Baked.TextureFilenames[0], out int textureSubid, out _); cmpt.Baked.TextureSubId = textureSubid; - }, - damageEffect + } ); @@ -915,9 +919,9 @@ public override bool TryGiveItemStack(ItemStack itemstack) } - public override void OnLoadCollectibleMappings(IWorldAccessor worldForResolve, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed) + public override void OnLoadCollectibleMappings(IWorldAccessor worldForResolve, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, int schematicSeed, bool resolveImports) { - base.OnLoadCollectibleMappings(worldForResolve, oldBlockIdMapping, oldItemIdMapping, schematicSeed); + base.OnLoadCollectibleMappings(worldForResolve, oldBlockIdMapping, oldItemIdMapping, schematicSeed, resolveImports); if (GearInventory != null) { diff --git a/Common/Entity/EntityBehavior.cs b/Common/Entity/EntityBehavior.cs index d6af5dc4..a5d0ecf3 100644 --- a/Common/Entity/EntityBehavior.cs +++ b/Common/Entity/EntityBehavior.cs @@ -101,6 +101,7 @@ public virtual void OnFallToGround(Vec3d lastTerrainContact, double withYMotion) /// The amount of saturation recieved. /// The category of food recieved. /// The delay before the loss of saturation. + /// public virtual void OnEntityReceiveSaturation(float saturation, EnumFoodCategory foodCat = EnumFoodCategory.Unknown, float saturationLossDelay = 10, float nutritionGainMultiplier = 1f) { @@ -197,7 +198,6 @@ public virtual void OnReceivedClientPacket(IServerPlayer player, int packetid, b /// /// The event fired when the client receives a packet. /// - /// /// /// /// @@ -237,8 +237,14 @@ public virtual void OnStoreCollectibleMappings(Dictionary bl { } - + + [Obsolete("Use the variant with resolveImports parameter")] public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping) + { + OnLoadCollectibleMappings(worldForNewMappings, oldItemIdMapping, oldItemIdMapping, true); + } + + public virtual void OnLoadCollectibleMappings(IWorldAccessor worldForNewMappings, Dictionary oldBlockIdMapping, Dictionary oldItemIdMapping, bool resolveImports) { } @@ -256,5 +262,13 @@ public virtual void FromBytes(bool isSync) { } + + /// + /// Can be used by the /entity command or maybe other commands, to test behaviors + ///
The argument will be an object provided by TextCommandCallingArgs, which can then be cast to the desired type e.g. int + ///
+ public virtual void TestCommand(object arg) + { + } } } diff --git a/Common/Entity/EntityChunky.cs b/Common/Entity/EntityChunky.cs index 33f31a16..228828bb 100644 --- a/Common/Entity/EntityChunky.cs +++ b/Common/Entity/EntityChunky.cs @@ -1,6 +1,4 @@ -using ProperVersion; -using System; -using System.IO; +using System.IO; using Vintagestory.API.Client; using Vintagestory.API.Common.Entities; using Vintagestory.API.Config; @@ -33,7 +31,7 @@ public override bool ApplyGravity get { return false; } } - public EntityChunky() : base(GlobalConstants.DefaultTrackingRange) // we call a parameterised constructor instead of the parameterless base constructor + public EntityChunky() : base(GlobalConstants.DefaultSimulationRange) // we call a parameterised constructor instead of the parameterless base constructor { Stats = new EntityStats(this); WatchedAttributes.SetAttribute("dim", new IntAttribute()); diff --git a/Common/Entity/EntityControls.cs b/Common/Entity/EntityControls.cs index 271b3ce5..b1de53df 100644 --- a/Common/Entity/EntityControls.cs +++ b/Common/Entity/EntityControls.cs @@ -13,6 +13,10 @@ namespace Vintagestory.API.Common /// public enum EnumEntityAction { + /// + /// No action - used when setting preCondition + /// + None = -1, /// /// Walk forwards /// diff --git a/Common/Entity/EntityItem.cs b/Common/Entity/EntityItem.cs index a88a4b2f..e669c614 100644 --- a/Common/Entity/EntityItem.cs +++ b/Common/Entity/EntityItem.cs @@ -70,7 +70,7 @@ public override byte[] LightHsv - public EntityItem() : base(GlobalConstants.DefaultTrackingRange * 3 / 4) // we call a parameterised constructor instead of the parameterless base constructor + public EntityItem() : base(GlobalConstants.DefaultSimulationRange * 3 / 4) // we call a parameterised constructor instead of the parameterless base constructor { Stats = new EntityStats(this); Slot = new EntityItemSlot(this); @@ -316,7 +316,7 @@ public static EntityItem FromItemstack(ItemStack itemstack, Vec3d position, Vec3 { EntityItem item = new EntityItem(); item.Code = GlobalConstants.EntityItemTypeCode; - item.SimulationRange = (int)(0.75f * GlobalConstants.DefaultTrackingRange); + item.SimulationRange = (int)(0.75f * GlobalConstants.DefaultSimulationRange); item.Itemstack = itemstack; item.ServerPos.SetPos(position); diff --git a/Common/Entity/EntityPlayer.cs b/Common/Entity/EntityPlayer.cs index 32bf5822..a19de67b 100644 --- a/Common/Entity/EntityPlayer.cs +++ b/Common/Entity/EntityPlayer.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using System.Numerics; +using System.Reflection.Metadata; using System.Text; using Vintagestory.API.Client; using Vintagestory.API.Common.Entities; @@ -80,6 +81,8 @@ public class EntityPlayer : EntityHumanoid public float walkSpeed = 1f; string[] randomIdleAnimations; + long lastInsideSoundTimeFinishTorso; + long lastInsideSoundTimeFinishLegs; public override float BodyYaw { get @@ -265,10 +268,50 @@ public IPlayer Player } } + IAnimationManager animManager; + IAnimationManager selfFpAnimManager; + bool IsSelf => PlayerUID == (Api as ICoreClientAPI)?.Settings.String["playeruid"]; + public bool selfNowShadowPass; + + public override IAnimationManager AnimManager + { + get + { + var capi = Api as ICoreClientAPI; + if (IsSelf && capi.Render.CameraType == EnumCameraMode.FirstPerson && !selfNowShadowPass) + { + return selfFpAnimManager; + } + + return animManager; + } + set { } + } + + public IAnimationManager OtherAnimManager + { + get + { + var capi = Api as ICoreClientAPI; + if (IsSelf && capi.Render.CameraType == EnumCameraMode.FirstPerson && !selfNowShadowPass) + { + return animManager; + } + + return selfFpAnimManager; + } + } + + public IAnimationManager TpAnimManager => animManager; public EntityPlayer() : base() { - AnimManager = new PlayerAnimationManager(); + // For the first person mode (and just for the current client player, not other players), we have animation sets with hands that are detached from the seraph body, + // which must only run while in first person mode and not while doing the shadow pass. + // To achieve this, we run two paralell animation managers and flip back and forth between the two as needed + animManager = new PlayerAnimationManager(); + (animManager as PlayerAnimationManager).UseFpAnmations = false; + selfFpAnimManager = new PlayerAnimationManager(); Stats .Register("healingeffectivness") @@ -298,7 +341,6 @@ public EntityPlayer() : base() .Register("temporalGearTLRepairCost", EnumStatBlendType.FlatSum) .Register("animalHarvestingTime") ; - } public override void Initialize(EntityProperties properties, ICoreAPI api, long chunkindex3d) @@ -325,6 +367,11 @@ public override void Initialize(EntityProperties properties, ICoreAPI api, long randomIdleAnimations = properties.Attributes["randomIdleAnimations"].AsArray(null); + + if (IsSelf) + { + OtherAnimManager.Init(api, this); + } } @@ -391,6 +438,12 @@ public override void OnTesselation(ref Shape entityShape, string shapePathForLog base.OnTesselation(ref entityShape, shapePathForLogging); AnimManager.HeadController = new PlayerHeadController(AnimManager, this, entityShape); + + if (IsSelf) + { + AnimationCache.InitManager(World.Api, OtherAnimManager, this, entityShape, OtherAnimManager.Animator?.RunningAnimations, "head"); + OtherAnimManager.HeadController = new PlayerHeadController(OtherAnimManager, this, entityShape); + } } private void updateEyeHeight(float dt) @@ -403,11 +456,9 @@ private void updateEyeHeight(float dt) var controls = MountedOn != null ? MountedOn.Controls : servercontrols; PrevFrameCanStandUp = !controls.Sneak && canStandUp(); - bool moving = (controls.TriesToMove && SidedPos.Motion.LengthSq() > 0.00001) && !controls.NoClip && !controls.DetachedMode && OnGround; + bool moving = (controls.TriesToMove && SidedPos.Motion.LengthSq() > 0.00001) && !controls.NoClip && !controls.DetachedMode; + bool walking = moving && OnGround; double newEyeheight = Properties.EyeHeight; - - - double newModelHeight = Properties.CollisionBoxSize.Y; if (controls.FloorSitting) @@ -445,17 +496,16 @@ private void updateEyeHeight(float dt) // Immersive fp mode has its own way of setting the eye pos // but we still need to run above non-ifp code for the hitbox - if (player.ImmersiveFpMode) + if (player.ImmersiveFpMode || !Alive) { secondsDead = Alive ? 0 : secondsDead + dt; updateLocalEyePosImmersiveFpMode(); } - double frequency = dt * controls.MovespeedMultiplier * GetWalkSpeedMultiplier(0.3) * (controls.Sprint ? 0.9 : 1.2) * (controls.Sneak ? 1.2f : 1); - walkCounter = moving ? walkCounter + frequency : 0; + walkCounter = walking ? walkCounter + frequency : 0; walkCounter = walkCounter % GameMath.TWOPI; double sneakDiv = (controls.Sneak ? 5 : 1.8); @@ -477,66 +527,126 @@ private void updateEyeHeight(float dt) if (moving) { - if (stepHeight > prevStepHeight) + bool playingInsideSound = PlayInsideSound(player); + + if (walking) { - if (direction == -1) + if (stepHeight > prevStepHeight) + { + if (direction == -1) + { + PlayStepSound(player, playingInsideSound); + } + direction = 1; + } + else { - PlayWalkSound(player); + direction = -1; } - direction = 1; } - else + } + + prevStepHeight = stepHeight; + } + } + + const int overlapPercentage = 10; // Hard-coding crime! + + public virtual Block GetInsideTorsoBlockSoundSource(BlockPos tmpPos) + { + // We look for a block around the player's torso height, typically leaves are at height 1 above the ground + return GetNearestBlockSoundSource(tmpPos, +1.1, BlockLayersAccess.Solid, false); + } + + public virtual Block GetInsideLegsBlockSoundSource(BlockPos tmpPos) + { + // We look for a block around the player's mid-leg height, deliberately chosen to be above the height of small ground clutter like loose stones (0.125 height) + return GetNearestBlockSoundSource(tmpPos, +0.2, BlockLayersAccess.Solid, false); + } + + public virtual bool PlayInsideSound(IPlayer player) + { + if (Swimming) return false; // We don't play solid block's inside sounds while swimming + + BlockPos tmpPos = new BlockPos((int)Pos.X, (int)Pos.Y, (int)Pos.Z, Pos.Dimension); + AssetLocation soundinsideTorso = GetInsideTorsoBlockSoundSource(tmpPos)?.GetSounds(Api.World.BlockAccessor, tmpPos).Inside; + AssetLocation soundinsideLegs = GetInsideLegsBlockSoundSource(tmpPos)?.GetSounds(Api.World.BlockAccessor, tmpPos).Inside; + + bool makingSound = false; + if (soundinsideTorso != null) + { + if (Api.Side == EnumAppSide.Client) + { + long timeNow = Environment.TickCount; + if (timeNow > lastInsideSoundTimeFinishTorso) { - direction = -1; + float volume = controls.Sneak ? 0.25f : 1f; + int duration = PlaySound(player, soundinsideTorso, 12, volume, 1.4); + lastInsideSoundTimeFinishTorso = timeNow + duration * (100 - overlapPercentage) / 100; } } + makingSound = true; + } - prevStepHeight = stepHeight; + if (soundinsideLegs != null && soundinsideLegs != soundinsideTorso) + { + if (Api.Side == EnumAppSide.Client) + { + long timeNow = Environment.TickCount; + if (timeNow > lastInsideSoundTimeFinishLegs) + { + float volume = controls.Sneak ? 0.35f : 1f; + int duration = PlaySound(player, soundinsideLegs, 12, volume, 0.6); + lastInsideSoundTimeFinishLegs = timeNow + duration * (100 - overlapPercentage) / 100; + } + } + makingSound = true; } + + return makingSound; } - public virtual void PlayWalkSound(IPlayer player) + public virtual void PlayStepSound(IPlayer player, bool playingInsideSound) { float volume = controls.Sneak ? 0.5f : 1f; EntityPos pos = SidedPos; - BlockPos tmpPos = new BlockPos((int)pos.X, (int)(pos.Y - 0.1f), (int)pos.Z, pos.Dimension); - var blockUnder = BlockUnderPlayer(player, pos, tmpPos); - var blockInside = World.BlockAccessor.GetBlock((int)pos.X, (int)(pos.Y + 0.1f), (int)pos.Z, BlockLayersAccess.Solid); - var liquidblockInside = World.BlockAccessor.GetBlock((int)pos.X, (int)(pos.Y + 0.1f), (int)pos.Z, BlockLayersAccess.Fluid); + BlockPos tmpPos = new BlockPos((int)pos.X, (int)pos.Y, (int)pos.Z, pos.Dimension); - AssetLocation soundwalk = blockUnder.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk; - tmpPos.Set((int)pos.X, (int)(pos.Y + 0.1f), (int)pos.Z); + var soundWalkLoc = + GetNearestBlockSoundSource(tmpPos, -0.03, BlockLayersAccess.MostSolid, true)?.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk ?? + GetNearestBlockSoundSource(tmpPos, -0.7, BlockLayersAccess.MostSolid, true)?.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk // When stepping stairs, seraphs is a bit floaty. And for fences we need to peek further down to find the block + ; - AssetLocation soundinside = blockInside.GetSounds(Api.World.BlockAccessor, tmpPos)?.Inside; + tmpPos.Set((int)pos.X, (int)(pos.Y + 0.1f), (int)pos.Z); + var liquidblockInside = World.BlockAccessor.GetBlock(tmpPos, BlockLayersAccess.Fluid); AssetLocation soundinsideliquid = liquidblockInside.GetSounds(Api.World.BlockAccessor, tmpPos)?.Inside; - bool isSelf = player.PlayerUID == (Api as ICoreClientAPI)?.World.Player?.PlayerUID; - var srvplayer = player as IServerPlayer; // Don't send the sound to the current player, he plays the sound himself - double x = 0, y = 0, z = 0; - if (!isSelf) { x = Pos.X; y = Pos.Y + 1; z = Pos.Z; } - if (soundinsideliquid != null) { - World.PlaySoundAt(soundinsideliquid, x, y, z, srvplayer, true, 12, volume); + PlaySound(player, soundinsideliquid, 12, volume, 1); } - if (!Swimming && soundwalk != null) + if (!Swimming && soundWalkLoc != null) { - if (blockInside.Id != blockUnder.Id && soundinside != null) - { - World.PlaySoundAt(soundwalk, x, y, z, srvplayer, true, 12, volume * 0.5f); - World.PlaySoundAt(soundinside, x, y, z, srvplayer, true, 12, volume); - } - else - { - World.PlaySoundAt(soundwalk, x, y, z, srvplayer, true, 12, volume); - } - + PlaySound(player, soundWalkLoc, 12, playingInsideSound ? volume * 0.5f : volume, 0); // Need to half volume if inside sounds as well OnFootStep?.Invoke(); } } + private int PlaySound(IPlayer player, AssetLocation sound, int range, float volume, double yOffset) + { + bool isSelf = player.PlayerUID == (Api as ICoreClientAPI)?.World.Player?.PlayerUID; + var srvplayer = player as IServerPlayer; // Don't send the sound to the current player, he plays the sound himself + double x = 0, y = 0, z = 0; + if (!isSelf) { x = Pos.X; y = Pos.Y + yOffset; z = Pos.Z; } + + if (Api.Side == EnumAppSide.Client) return ((IClientWorldAccessor)World).PlaySoundAtAndGetDuration(sound, x, y, z, srvplayer, true, range, volume); + + World.PlaySoundAt(sound, x, y, z, srvplayer, true, range, volume); + return 0; + } + public override void OnGameTick(float dt) { // Update every tick in case this is not current with the stats value for some reason - this will then be accessed multiple times by the locomotors later in the tick @@ -572,7 +682,7 @@ public override void OnGameTick(float dt) base.OnGameTick(dt); } - if (!servercontrols.TriesToMove && !controls.IsFlying && !controls.Gliding && RightHandItemSlot?.Empty == true) + if (!servercontrols.TriesToMove && !controls.IsFlying && !controls.Gliding && RightHandItemSlot?.Empty == true && !Swimming) { secondsIdleAccum += dt; if (secondsIdleAccum > 20 && World.Rand.NextDouble() < 0.004) @@ -769,8 +879,8 @@ protected void protectEyesFromWind(float dt) bool lookingIntoWind = Math.Abs(yawDiff) < 45 * GameMath.DEG2RAD; bool isOutside = GlobalConstants.CurrentDistanceToRainfallClient < 6; - if (isOutside && lookingIntoWind && RightHandItemSlot?.Empty == true && strongWindAccum > 2 && Player.WorldData.CurrentGameMode != EnumGameMode.Creative) - { + if (isOutside && lookingIntoWind && RightHandItemSlot?.Empty == true && strongWindAccum > 2 && Player.WorldData.CurrentGameMode != EnumGameMode.Creative && !hasEyeProtectiveGear()) + { AnimManager.StartAnimation("protecteyes"); } else @@ -780,6 +890,11 @@ protected void protectEyesFromWind(float dt) } } + private bool hasEyeProtectiveGear() + { + return GearInventory != null && GearInventory.FirstOrDefault((slot) => !slot.Empty && slot.Itemstack.Collectible.Attributes?.IsTrue("eyeprotective") == true) != null; + } + private bool canStandUp() { tmpCollBox.Set(SelectionBox); @@ -889,11 +1004,16 @@ public override void OnFallToGround(double motionY) { EntityPos pos = SidedPos; BlockPos tmpPos = new BlockPos((int)pos.X, (int)(pos.Y - 0.1f), (int)pos.Z, pos.Dimension); - var blockUnder = BlockUnderPlayer(player, pos, tmpPos); - AssetLocation soundwalk = blockUnder.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk; - if (soundwalk != null && !Swimming) + var blockUnder = GetNearestBlockSoundSource(tmpPos, -0.1, BlockLayersAccess.MostSolid, true); + + var soundWalkLoc = + GetNearestBlockSoundSource(tmpPos, -0.1, BlockLayersAccess.MostSolid, true)?.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk ?? + GetNearestBlockSoundSource(tmpPos, -0.7, BlockLayersAccess.MostSolid, true)?.GetSounds(Api.World.BlockAccessor, tmpPos)?.Walk // Stairs and Fences are a special snowflake + ; + + if (soundWalkLoc != null && !Swimming) { - World.PlaySoundAt(soundwalk, this, player, true, 12, 1.5f); + World.PlaySoundAt(soundWalkLoc, this, player, true, 12, 1.5f); } OnImpact?.Invoke(motionY); @@ -904,42 +1024,79 @@ public override void OnFallToGround(double motionY) - internal Block BlockUnderPlayer(IPlayer player, EntityPos pos, BlockPos tmpPos) + /// + /// Returns null if there is no nearby sound source + /// + /// Might get intentionally modified if the nearest sound source the player is intersecting with is in an adjacent block + /// + /// + /// + /// + public Block GetNearestBlockSoundSource(BlockPos tmpPos, double yOffset, int blockLayer, bool usecollisionboxes) { + EntityPos pos = SidedPos; Cuboidd entityBox = new Cuboidd(); - entityBox.SetAndTranslate(player.Entity.CollisionBox, pos.X % 1.0, (pos.Y - 0.1) % 1.0, pos.Z % 1.0); + Cuboidf colBox = CollisionBox; + entityBox.SetAndTranslate(colBox, pos.X, pos.Y + yOffset, pos.Z); + entityBox.GrowBy(-0.001, 0, -0.001); // Prevent detection when hardly inside, just touching (movement system or rounding errors can push an entity fractionally inside a block) + + + int yo = (int)(pos.Y + yOffset); + tmpPos.Set(pos.XInt, yo, pos.ZInt); + Block block = getSoundSourceBlockAt(entityBox, tmpPos, blockLayer, usecollisionboxes); + if (block != null) return block; + - Block block = feetIntersectingBlock(tmpPos, entityBox); + double xdistToBlockCenter = GameMath.Mod(pos.X, 1.0) - 0.5; + double zdistToBlockCenter = GameMath.Mod(pos.Z, 1.0) - 0.5; - // Deal with situation where the player is walking along the edge of a block, which is partially under the player (e.g. left foot over air, right foot over a block) - if (block.Id == 0) + int adjacentX = pos.XInt + Math.Sign(xdistToBlockCenter); + int adjacentZ = pos.ZInt + Math.Sign(zdistToBlockCenter); + + // A bit of extra code to prioritise the block in x-direction if that one is closer than the block in z-direction and vice versa + int nearerNeibX, furtherNeibX, nearerNeibZ, furtherNeibZ; + if (Math.Abs(xdistToBlockCenter) > Math.Abs(zdistToBlockCenter)) + { + nearerNeibX = adjacentX; + nearerNeibZ = pos.ZInt; + + furtherNeibX = pos.XInt; + furtherNeibZ = adjacentZ; + } else { - // Figure out the next-nearest neighbouring block position to the player's current position - float dx = (float)(GameMath.Mod(pos.X, 1.0) - 0.5); // ensure pos.X is not negative so that dx always in the range -0.5 to 0.5 - float dz = (float)(GameMath.Mod(pos.Z, 1.0) - 0.5); // ensure pos.Z is not negative so that dz always in the range -0.5 to 0.5 - int idx = Math.Sign(dx); - int idz = Math.Sign(dz); - if (Math.Abs(dx) > Math.Abs(dz)) idz = 0; else idx = 0; // Set to zero whichever one is smaller + nearerNeibX = pos.XInt; + nearerNeibZ = adjacentZ; - tmpPos.Add(idx, 0, idz); // This will affect the pos in the calling code too, which is intended - block = feetIntersectingBlock(tmpPos, entityBox); + furtherNeibX = adjacentX; + furtherNeibZ = pos.ZInt; } - return block; + return + getSoundSourceBlockAt(entityBox, tmpPos.Set(nearerNeibX, yo, nearerNeibZ), blockLayer, usecollisionboxes) ?? + getSoundSourceBlockAt(entityBox, tmpPos.Set(furtherNeibX, yo, furtherNeibZ), blockLayer, usecollisionboxes) ?? + getSoundSourceBlockAt(entityBox, tmpPos.Set(adjacentX, yo, adjacentZ), blockLayer, usecollisionboxes) + ; } - protected Block feetIntersectingBlock(BlockPos tmpPos, Cuboidd entityBox) + + protected Block getSoundSourceBlockAt(Cuboidd entityBox, BlockPos tmpPos, int blockLayer, bool usecollisionboxes) { - Block block = World.BlockAccessor.GetBlock(tmpPos, BlockLayersAccess.MostSolid); - Cuboidf[] collisionBoxes = block.GetCollisionBoxes(World.BlockAccessor, tmpPos); - if (collisionBoxes == null) return World.GetBlock(0); - for (int i = 0; i < collisionBoxes.Length; i++) + Block block = World.BlockAccessor.GetBlock(tmpPos, blockLayer); + if (!usecollisionboxes && block.GetSounds(Api.World.BlockAccessor, tmpPos)?.Inside == null) return null; + + Cuboidf[] blockBoxes = usecollisionboxes ? block.GetCollisionBoxes(World.BlockAccessor, tmpPos) : block.GetSelectionBoxes(World.BlockAccessor, tmpPos); + if (blockBoxes == null) return null; + + for (int i = 0; i < blockBoxes.Length; i++) { - Cuboidf blockBox = collisionBoxes[i]; - if (blockBox == null) continue; - if (entityBox.Intersects(blockBox)) return block; + Cuboidf blockBox = blockBoxes[i]; + if (blockBox != null && entityBox.Intersects(blockBox, tmpPos.X, tmpPos.Y, tmpPos.Z)) + { + return block; + } } - return World.GetBlock(0); + + return null; } @@ -1012,8 +1169,6 @@ public override void Revive() public override void PlayEntitySound(string type, IPlayer dualCallByPlayer = null, bool randomizePitch = true, float range = 24) { - ICoreClientAPI capi = Api as ICoreClientAPI; - if (type == "hurt") { talkUtil?.Talk(EnumTalkType.Hurt2); @@ -1211,7 +1366,7 @@ public override void TeleportToDouble(double x, double y, double z, Action onTel ICoreServerAPI sapi = this.World.Api as ICoreServerAPI; if (sapi != null) { - sapi.WorldManager.LoadChunkColumnPriority((int)ServerPos.X / World.BlockAccessor.ChunkSize, (int)ServerPos.Z / World.BlockAccessor.ChunkSize, new ChunkLoadOptions() + sapi.WorldManager.LoadChunkColumnPriority((int)ServerPos.X / GlobalConstants.ChunkSize, (int)ServerPos.Z / GlobalConstants.ChunkSize, new ChunkLoadOptions() { OnLoaded = () => { @@ -1224,7 +1379,7 @@ public override void TeleportToDouble(double x, double y, double z, Action onTel { sapi.Network.BroadcastEntityPacket(EntityId, 1, SerializerUtil.Serialize(ServerPos.XYZ)); IServerPlayer player = this.Player as IServerPlayer; - int chunksize = World.BlockAccessor.ChunkSize; + int chunksize = GlobalConstants.ChunkSize; player.CurrentChunkSentRadius = 0; sapi.Event.RegisterCallback((bla) => { @@ -1292,5 +1447,34 @@ public override void FromBytes(BinaryReader reader, bool forClient) walkSpeed = Stats.GetBlended("walkspeed"); } + public override void UpdateDebugAttributes() + { + base.UpdateDebugAttributes(); + + string anims = ""; + int i = 0; + foreach (string anim in OtherAnimManager.ActiveAnimationsByAnimCode.Keys) + { + if (i++ > 0) anims += ","; + anims += anim; + } + + i = 0; + StringBuilder runninganims = new StringBuilder(); + if (OtherAnimManager.Animator != null) + { + foreach (var anim in OtherAnimManager.Animator.RunningAnimations) + { + if (!anim.Active) continue; + + if (i++ > 0) runninganims.Append(","); + runninganims.Append(anim.Animation.Code); + } + + DebugAttributes.SetString("Other Active Animations", anims.Length > 0 ? anims : "-"); + DebugAttributes.SetString("Other Running Animations", runninganims.Length > 0 ? runninganims.ToString() : "-"); + } + } + } } diff --git a/Common/Entity/EntityRenderer.cs b/Common/Entity/EntityRenderer.cs index 3f5364c6..8ed50b28 100644 --- a/Common/Entity/EntityRenderer.cs +++ b/Common/Entity/EntityRenderer.cs @@ -70,8 +70,6 @@ public virtual void DoRender2D(float dt) { } /// /// /// - /// - /// public virtual void RenderToGui(float dt, double posX, double posY, double posZ, float yawDelta, float size) { } diff --git a/Common/Entity/Player/IPlayerInventoryManager.cs b/Common/Entity/Player/IPlayerInventoryManager.cs index 820e03e9..81673a95 100644 --- a/Common/Entity/Player/IPlayerInventoryManager.cs +++ b/Common/Entity/Player/IPlayerInventoryManager.cs @@ -102,14 +102,6 @@ public interface IPlayerInventoryManager /// ItemStack GetHotbarItemstack(int slotId); - /// - /// Returns a slot that would best fit the contents of the source slot. Only tries to place the itemstack into the hotbar. - /// - /// - /// - /// - //ItemSlot GetBestSuitedHotbarSlot(IInventory sourceInventory, ItemSlot sourceSlot); - /// /// Returns the hotbar inventory object. Obvious comment is being obvious. /// @@ -130,6 +122,7 @@ public interface IPlayerInventoryManager /// /// /// + /// /// /// ItemSlot GetBestSuitedSlot(ItemSlot sourceSlot, bool onlyPlayerInventory, ItemStackMoveOperation op = null, List skipSlots = null); diff --git a/Common/Entity/PlayerAnimationManager.cs b/Common/Entity/PlayerAnimationManager.cs index 2cfe3445..f8fdebb2 100644 --- a/Common/Entity/PlayerAnimationManager.cs +++ b/Common/Entity/PlayerAnimationManager.cs @@ -1,5 +1,4 @@ -using System; -using Vintagestory.API.Client; +using Vintagestory.API.Client; using Vintagestory.API.Common.Entities; using Vintagestory.API.Datastructures; @@ -7,9 +6,20 @@ namespace Vintagestory.API.Common { public class PlayerAnimationManager : AnimationManager { - bool isSelf => capi.World.Player.Entity.EntityId == entity.EntityId && capi.World.Player.CameraMode == EnumCameraMode.FirstPerson; + public bool UseFpAnmations=true; - string fpEnding => capi?.World.Player.CameraMode == EnumCameraMode.FirstPerson ? ((api as ICoreClientAPI)?.Settings.Bool["immersiveFpMode"] == true ? "-ifp" : "-fp") : ""; + bool useFpAnimSet => UseFpAnmations && api.Side == EnumAppSide.Client && capi.World.Player.Entity.EntityId == entity.EntityId && capi.World.Player.CameraMode == EnumCameraMode.FirstPerson; + + string fpEnding => UseFpAnmations && capi?.World.Player.CameraMode == EnumCameraMode.FirstPerson ? ((api as ICoreClientAPI)?.Settings.Bool["immersiveFpMode"] == true ? "-ifp" : "-fp") : ""; + + EntityPlayer plrEntity; + + public override void Init(ICoreAPI api, Entity entity) + { + base.Init(api, entity); + + plrEntity = entity as EntityPlayer; + } public override void OnClientFrame(float dt) { @@ -19,6 +29,11 @@ public override void OnClientFrame(float dt) { startHeldReadyAnimIfMouseUp(); } + + if (useFpAnimSet) + { + plrEntity.TpAnimManager.OnClientFrame(dt); + } } @@ -33,9 +48,14 @@ public override bool StartAnimation(string configCode) { if (configCode == null) return false; + if (useFpAnimSet) + { + plrEntity.TpAnimManager.StartAnimation(configCode); + } + AnimationMetaData animdata; - if (entity.Properties.Client.AnimationsByMetaCode.TryGetValue(configCode + fpEnding, out animdata)) + if (useFpAnimSet && entity.Properties.Client.AnimationsByMetaCode.TryGetValue(configCode + fpEnding, out animdata)) { StartAnimation(animdata); return true; @@ -46,8 +66,10 @@ public override bool StartAnimation(string configCode) public override bool StartAnimation(AnimationMetaData animdata) { - if (api.Side == EnumAppSide.Client && isSelf && !animdata.Code.EndsWith(fpEnding)) + if (useFpAnimSet && !animdata.Code.EndsWith(fpEnding)) { + plrEntity.TpAnimManager.StartAnimation(animdata); + if (entity.Properties.Client.AnimationsByMetaCode.TryGetValue(animdata.Code + fpEnding, out var animdatafp)) { if (ActiveAnimationsByAnimCode.TryGetValue(animdatafp.Animation, out var activeAnimdata) && activeAnimdata == animdatafp) return false; @@ -60,7 +82,7 @@ public override bool StartAnimation(AnimationMetaData animdata) public override void RegisterFrameCallback(AnimFrameCallback trigger) { - if (api.Side == EnumAppSide.Client && isSelf && !trigger.Animation.EndsWith(fpEnding) && entity.Properties.Client.AnimationsByMetaCode.ContainsKey(trigger.Animation + fpEnding)) + if (useFpAnimSet && !trigger.Animation.EndsWith(fpEnding) && entity.Properties.Client.AnimationsByMetaCode.ContainsKey(trigger.Animation + fpEnding)) { trigger.Animation += fpEnding; } @@ -71,7 +93,13 @@ public override void StopAnimation(string code) { if (code == null) return; - string[] anims = new string[] { code, code + "-ifp", code + "-fp" }; + if (api.Side == EnumAppSide.Client) (plrEntity.OtherAnimManager as PlayerAnimationManager).StopSelfAnimation(code); + StopSelfAnimation(code); + } + + public void StopSelfAnimation(string code) + { + string[] anims = new string[] { code, code + "-ifp", code + "-fp" }; foreach (var anim in anims) { base.StopAnimation(anim); @@ -80,7 +108,7 @@ public override void StopAnimation(string code) public override bool IsAnimationActive(params string[] anims) { - if (api.Side == EnumAppSide.Client && isSelf) + if (useFpAnimSet) { foreach (var val in anims) { @@ -291,7 +319,8 @@ public override void FromAttributes(ITreeAttribute tree, string version) lastRunningHeldUseAnimation = tree.GetString("lrHeldUseAnim"); lastRunningHeldHitAnimation = tree.GetString("lrHeldHitAnim"); - lastRunningRightHeldIdleAnimation = tree.GetString("lrRightHeldIdleAnim"); + // Can we not have this line? It breaks fp hands when loading up with a world with a block in hands - the shoulds of the hands become visible when walking and looking down + //lastRunningRightHeldIdleAnimation = tree.GetString("lrRightHeldIdleAnim"); } public override void ToAttributes(ITreeAttribute tree, bool forClient) @@ -312,6 +341,5 @@ public override void ToAttributes(ITreeAttribute tree, bool forClient) } } - } } diff --git a/Common/IO/SQLiteDBConnection.cs b/Common/IO/SQLiteDBConnection.cs index fee6af82..6013da5b 100644 --- a/Common/IO/SQLiteDBConnection.cs +++ b/Common/IO/SQLiteDBConnection.cs @@ -1,14 +1,14 @@ using System; using System.Data; using System.Data.Common; -using System.Data.SQLite; using System.IO; +using Microsoft.Data.Sqlite; namespace Vintagestory.API.Common { public class SQLiteDBConnection : IDisposable { - protected SQLiteConnection sqliteConn; + protected SqliteConnection sqliteConn; protected string databaseFileName; protected ILogger logger; public object transactionLock = new object(); @@ -48,44 +48,43 @@ public bool OpenOrCreate(string filename, ref string errorMessage, bool requireW try { - DbConnectionStringBuilder conf = new DbConnectionStringBuilder(); - conf.Add("Data Source", databaseFileName); - conf.Add("Version", "3"); - conf.Add("New", "True"); // Create new file if it doesnt exist - conf.Add("Compress", "True"); - - if (corruptionProtection) - { - conf.Add("Journal Mode", "WAL"); - conf.Add("Synchronous", "Normal"); - } else - { - conf.Add("Journal Mode", "Off"); - } + var conf = new DbConnectionStringBuilder { + { "Data Source", databaseFileName }, + { "Pooling","false" } + }; if (!requireWriteAccess) { - conf.Add("read only", "True"); + conf.Add("Mode", "ReadOnly"); } - - sqliteConn = new SQLiteConnection(conf.ToString()); - + + sqliteConn = new SqliteConnection(conf.ToString()); sqliteConn.Open(); + + using (var cmd = sqliteConn.CreateCommand()) + { + if (corruptionProtection) + { + cmd.CommandText = "PRAGMA journal_mode=WAL;PRAGMA synchronous=Normal;"; + } + else + { + cmd.CommandText = "PRAGMA journal_mode=Off;"; + } + + cmd.ExecuteNonQuery(); + } + if (requireWriteAccess) { CreateTablesIfNotExists(sqliteConn); } - - if (doIntegrityCheck) + if (doIntegrityCheck && !DoIntegrityCheck(sqliteConn)) { - if (!DoIntegrityCheck(sqliteConn)) - { - logger.Error(errorMessage = "Database is possibly corrupted."); - } + logger.Error(errorMessage = "Database is possibly corrupted."); } - } catch (Exception e) { @@ -117,14 +116,14 @@ public virtual void Dispose() } - protected virtual void CreateTablesIfNotExists(SQLiteConnection sqliteConn) + protected virtual void CreateTablesIfNotExists(SqliteConnection sqliteConn) { // Create your tables here } public void Vacuum() { - using (SQLiteCommand command = sqliteConn.CreateCommand()) + using (var command = sqliteConn.CreateCommand()) { command.CommandText = "vacuum;"; command.ExecuteNonQuery(); @@ -132,14 +131,14 @@ public void Vacuum() } - public bool DoIntegrityCheck(SQLiteConnection sqliteConn, bool logResults = true) + public bool DoIntegrityCheck(SqliteConnection sqliteConn, bool logResults = true) { - bool okay = false; - using (SQLiteCommand command = sqliteConn.CreateCommand()) + var okay = false; + using (var command = sqliteConn.CreateCommand()) { command.CommandText = "PRAGMA integrity_check"; - using (SQLiteDataReader sqlite_datareader = command.ExecuteReader()) + using (var sqlite_datareader = command.ExecuteReader()) { if (logResults) logger.Notification(string.Format("Database: {0}. Running SQLite integrity check:", sqliteConn.DataSource)); @@ -170,7 +169,7 @@ public static bool HaveWriteAccessFolder(string folderPath) try { - string testfilename = Path.Combine(folderPath, "temp.txt"); + var testfilename = Path.Combine(folderPath, "temp.txt"); File.Create(testfilename).Close(); File.Delete(testfilename); return true; @@ -214,7 +213,7 @@ public static bool HaveWriteAccessFile(FileInfo file) protected DbParameter CreateParameter(string parameterName, DbType dbType, object value, DbCommand command) { - DbParameter p = command.CreateParameter(); + var p = command.CreateParameter(); p.ParameterName = parameterName; p.DbType = dbType; p.Value = value; diff --git a/Common/Inventory/IInventory.cs b/Common/Inventory/IInventory.cs index bcf232ae..fb37b389 100644 --- a/Common/Inventory/IInventory.cs +++ b/Common/Inventory/IInventory.cs @@ -83,6 +83,7 @@ public interface IInventory : IReadOnlyCollection /// Weight could be 10 for an empty armor slot and the source slot contains an armor itemtack /// /// + /// /// /// WeightedSlot GetBestSuitedSlot(ItemSlot sourceSlot, ItemStackMoveOperation op = null, List skipSlots = null); diff --git a/Common/Inventory/InventoryBase.cs b/Common/Inventory/InventoryBase.cs index 8161cfdc..9cce4efa 100644 --- a/Common/Inventory/InventoryBase.cs +++ b/Common/Inventory/InventoryBase.cs @@ -810,7 +810,7 @@ public virtual float GetTransitionSpeedMul(EnumTransitionType transType, ItemSta protected virtual float GetDefaultTransitionSpeedMul(EnumTransitionType transType) { - return (transType == EnumTransitionType.Perish || transType == EnumTransitionType.Cure || transType == EnumTransitionType.Dry) ? 1 : 0; + return (transType == EnumTransitionType.Perish || transType == EnumTransitionType.Cure || transType == EnumTransitionType.Dry || transType == EnumTransitionType.Melt) ? 1 : 0; } /// diff --git a/Common/Inventory/InventoryGeneric.cs b/Common/Inventory/InventoryGeneric.cs index 1ef081e5..fec1d889 100644 --- a/Common/Inventory/InventoryGeneric.cs +++ b/Common/Inventory/InventoryGeneric.cs @@ -111,7 +111,12 @@ public override void FromTreeAttributes(ITreeAttribute treeAttribute) slots = SlotsFromTreeAttributes(treeAttribute, slots); int add = cnt - slots.Length; - while (add-- > 0) + AddSlots(add); + } + + public void AddSlots(int amount) + { + while (amount-- > 0) { slots = slots.Append(NewSlot(slots.Length)); } diff --git a/Common/Inventory/ItemSlot.cs b/Common/Inventory/ItemSlot.cs index 746ac67f..0a652056 100644 --- a/Common/Inventory/ItemSlot.cs +++ b/Common/Inventory/ItemSlot.cs @@ -90,6 +90,7 @@ public virtual int GetRemainingSlotSpace(ItemStack forItemstack) /// Whether or not this slot can take the item from the source slot. /// /// + /// /// public virtual bool CanTakeFrom(ItemSlot sourceSlot, EnumMergePriority priority = EnumMergePriority.AutoMerge) { diff --git a/Common/Model/Animation/Animation.cs b/Common/Model/Animation/Animation.cs index 7beae980..f06458a8 100644 --- a/Common/Model/Animation/Animation.cs +++ b/Common/Model/Animation/Animation.cs @@ -110,7 +110,8 @@ public void GenerateAllFrames(ShapeElement[] rootElements, Dictionary= GlobalConstants.MaxAnimatedElements) { - throw new Exception("Max joint cap of "+ GlobalConstants.MaxAnimatedElements + " reached. Sorry, you'll have to simplify your model if you want it to be animated. (until some programmer finds another solution to pass on more joint data through shader uniforms)"); + if (GlobalConstants.MaxAnimatedElements < 46 && jointsById.Count <= 46) throw new Exception("Max joint cap of " + GlobalConstants.MaxAnimatedElements + " reached, needs to be at least " + jointsById.Count + ". In clientsettings.json, please try increasing the \"maxAnimatedElements\": setting to 46. This works for most GPUs. Otherwise you might need to disable the creature."); + throw new Exception("A mod's entity has " + jointsById.Count + " animations which exceeds the max joint cap of "+ GlobalConstants.MaxAnimatedElements + ". Sorry, you'll have to either disable this creature or simplify the model."); } for (int i = 0; i < resolvedKeyFrames.Length; i++) diff --git a/Common/Model/Animation/AnimationCache.cs b/Common/Model/Animation/AnimationCache.cs index 880a0c69..6ff3fd6c 100644 --- a/Common/Model/Animation/AnimationCache.cs +++ b/Common/Model/Animation/AnimationCache.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Vintagestory.API.Common.Entities; using Vintagestory.API.Util; @@ -38,6 +37,7 @@ public static void ClearCache(ICoreAPI api) /// Clears the animation cache. /// /// + /// public static void ClearCache(ICoreAPI api, Entity entity) { var animCache = ObjectCacheUtil.GetOrCreate(api, "animCache", () => new Dictionary()); @@ -52,6 +52,7 @@ public static void ClearCache(ICoreAPI api, Entity entity) /// /// /// + /// /// /// public static IAnimationManager InitManager(ICoreAPI api, IAnimationManager manager, Entity entity, Shape entityShape, RunningAnimation[] copyOverAnims, params string[] requireJointsForElements) @@ -109,7 +110,7 @@ public static IAnimationManager InitManager(ICoreAPI api, IAnimationManager mana if (sourceAnim != null && sourceAnim.Active) { manager.ActiveAnimationsByAnimCode.TryGetValue(sourceAnim.Animation.Code, out var meta); - if (meta != null) + if (meta != null) { meta.StartFrameOnce = sourceAnim.CurrentFrame; } diff --git a/Common/Model/Animation/AnimationKeyFrame.cs b/Common/Model/Animation/AnimationKeyFrame.cs index f4e89788..e3a7bf8e 100644 --- a/Common/Model/Animation/AnimationKeyFrame.cs +++ b/Common/Model/Animation/AnimationKeyFrame.cs @@ -1,5 +1,4 @@ using Newtonsoft.Json; -using System; using System.Collections.Generic; namespace Vintagestory.API.Common diff --git a/Common/Model/Animation/AnimationManager.cs b/Common/Model/Animation/AnimationManager.cs index 647277cd..28980fd8 100644 --- a/Common/Model/Animation/AnimationManager.cs +++ b/Common/Model/Animation/AnimationManager.cs @@ -137,7 +137,7 @@ public virtual bool StartAnimation(string configCode) /// public virtual void StopAnimation(string code) { - if (code == null) return; + if (code == null || entity == null) return; if (entity.World.Side == EnumAppSide.Server) { @@ -291,11 +291,11 @@ public virtual void ToAttributes(ITreeAttribute tree, bool forClient) } - /// /// Loads the entity from a stored byte array from the SaveGame /// /// + /// public virtual void FromAttributes(ITreeAttribute tree, string version) { var animtree = tree["activeAnims"] as ITreeAttribute; diff --git a/Common/Model/Animation/IAnimator.cs b/Common/Model/Animation/IAnimator.cs index c38ec237..502acb80 100644 --- a/Common/Model/Animation/IAnimator.cs +++ b/Common/Model/Animation/IAnimator.cs @@ -125,12 +125,14 @@ public interface IAnimationManager : IDisposable /// Additional attributes applied to the animation /// /// + /// void FromAttributes(ITreeAttribute tree, string version); /// /// Additional attributes applied from the animation /// /// + /// void ToAttributes(ITreeAttribute tree, bool forClient); /// diff --git a/Common/Model/Shape/Shape.cs b/Common/Model/Shape/Shape.cs index 39198838..87ff4bae 100644 --- a/Common/Model/Shape/Shape.cs +++ b/Common/Model/Shape/Shape.cs @@ -4,8 +4,7 @@ using System.Linq; using Vintagestory.API.Util; using System.Runtime.Serialization; -using System.Collections; -using Vintagestory.API.Client; +using System.Xml.Linq; namespace Vintagestory.API.Common { @@ -102,22 +101,86 @@ public void ResolveReferences(ILogger errorLogger, string shapeNameForLogging) } } + + + /// + /// Prefixes texturePrefixCode to all textures in this shape. Required pre-step for stepparenting. The long arguments StepParentShape() calls this method. + /// + /// + /// + /// + /// + public bool SubclassForStepParenting(string texturePrefixCode, float damageEffect = 0f) + { + HashSet textureCodes = new HashSet(); + + foreach (var childElem in Elements) + { + childElem.WalkRecursive((el) => + { + el.DamageEffect = damageEffect; + + foreach (var face in el.FacesResolved) + { + if (face == null || !face.Enabled) continue; + textureCodes.Add(face.Texture); + + face.Texture = texturePrefixCode + "-" + face.Texture; + } + }); + } + + if (Textures != null) + { + var texturesizes = TextureSizes.ToArray(); + TextureSizes.Clear(); + foreach (var val in texturesizes) + { + TextureSizes[texturePrefixCode + "-" + val.Key] = val.Value; + textureCodes.Remove(val.Key); + } + + // Set default texturewidth/height for those not in the TextureSizes dict + foreach (var code in textureCodes) + { + TextureSizes[texturePrefixCode + "-" + code] = new int[] { TextureWidth, TextureHeight }; + } + } + + return true; + } + /// - /// Adds a step parented shape to this shape + /// Adds a step parented shape to this shape. If you plan to cache the childShape use the shorter argument method and call SubclassForStepParenting() only once on it /// /// - /// Used for logging and as texture prefix + /// /// /// /// + /// /// /// public bool StepParentShape(Shape childShape, string texturePrefixCode, string childLocationForLogging, string parentLocationForLogging, ILogger logger, Action onTexture, float damageEffect = 0) { - return StepParentShape(null, childShape.Elements, childShape, texturePrefixCode, childLocationForLogging, parentLocationForLogging, logger, onTexture, damageEffect); + childShape.SubclassForStepParenting(texturePrefixCode, damageEffect); + return StepParentShape(null, childShape.Elements, childShape, childLocationForLogging, parentLocationForLogging, logger, onTexture); } - private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, Shape childShape, string texturePrefixCode, string childLocationForLogging, string parentLocationForLogging, ILogger logger, Action onTexture, float damageEffect = 0) + /// + /// Adds a step parented shape to this shape, does not call the required pre-step SubclassForStepParenting() + /// + /// + /// + /// + /// + /// + public bool StepParentShape(Shape childShape, string childLocationForLogging, string parentLocationForLogging, ILogger logger, Action onTexture) + { + return StepParentShape(null, childShape.Elements, childShape, childLocationForLogging, parentLocationForLogging, logger, onTexture); + } + + private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, Shape childShape, string childLocationForLogging, string parentLocationForLogging, ILogger logger, Action onTexture) { bool anyElementAdded = false; foreach (var childElem in elements) @@ -126,7 +189,8 @@ private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, S if (childElem.Children != null) { - anyElementAdded |= StepParentShape(childElem, childElem.Children, childShape, texturePrefixCode, childLocationForLogging, parentLocationForLogging, logger, onTexture, damageEffect); + bool added = StepParentShape(childElem, childElem.Children, childShape, childLocationForLogging, parentLocationForLogging, logger, onTexture); ; + anyElementAdded |= added; } if (childElem.StepParentName != null) @@ -165,17 +229,6 @@ private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, S childElem.SetJointIdRecursive(stepparentElem.JointId); - childElem.WalkRecursive((el) => - { - el.DamageEffect = damageEffect; - - foreach (var face in el.FacesResolved) - { - if (face == null) continue; - face.Texture = texturePrefixCode + "-" + face.Texture; - } - }); - anyElementAdded = true; } @@ -188,7 +241,7 @@ private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, S { var entityAnim = Animations.FirstOrDefault(anim => anim.Code == gearAnim.Code); if (entityAnim == null) continue; - + for (int gi = 0; gi < gearAnim.KeyFrames.Length; gi++) { var gearKeyFrame = gearAnim.KeyFrames[gi]; @@ -202,7 +255,6 @@ private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, S } } - if (childShape.Textures != null) { foreach (var val in childShape.Textures) @@ -212,14 +264,13 @@ private bool StepParentShape(ShapeElement parentElem, ShapeElement[] elements, S foreach (var val in childShape.TextureSizes) { - TextureSizes[texturePrefixCode + "-" + val.Key] = val.Value; + TextureSizes[val.Key] = val.Value; } } return anyElementAdded; } - private AnimationKeyFrame getOrCreateKeyFrame(Animation entityAnim, int frame) { for (int ei = 0; ei < entityAnim.KeyFrames.Length; ei++) @@ -277,7 +328,9 @@ public void ResolveAndLoadJoints(params string[] requireJointsForElements) /// /// Resolves all joints and loads them. /// + /// /// + /// public void ResolveAndFindJoints(ILogger errorLogger, string shapeName, params string[] requireJointsForElements) { if (Animations == null) return; @@ -453,6 +506,7 @@ private void walkElements(ShapeElement[] elements, string wildcardpath, Action /// The name of the element to get. + /// /// The shape element or null if none was found public ShapeElement GetElementByName(string name, StringComparison stringComparison = StringComparison.InvariantCultureIgnoreCase) { diff --git a/Common/MultiblockStructure.cs b/Common/MultiblockStructure.cs index 88c1be3e..1f3c986c 100644 --- a/Common/MultiblockStructure.cs +++ b/Common/MultiblockStructure.cs @@ -100,6 +100,7 @@ public void WalkMatchingBlocks(IWorldAccessor world, BlockPos centerPos, Action< /// /// /// + /// /// public int InCompleteBlockCount(IWorldAccessor world, BlockPos centerPos, PositionMismatchDelegate onMismatch = null) { diff --git a/Common/Particle/IParticlePropertiesProvider.cs b/Common/Particle/IParticlePropertiesProvider.cs index e9f22a2b..e504158f 100644 --- a/Common/Particle/IParticlePropertiesProvider.cs +++ b/Common/Particle/IParticlePropertiesProvider.cs @@ -169,6 +169,7 @@ public interface IParticlePropertiesProvider /// For reading from the network /// /// + /// void FromBytes(BinaryReader reader, IWorldAccessor resolver); /// diff --git a/Common/Registry/RegistryObject.cs b/Common/Registry/RegistryObject.cs index 0cf64d74..0ad9a294 100644 --- a/Common/Registry/RegistryObject.cs +++ b/Common/Registry/RegistryObject.cs @@ -2,13 +2,13 @@ using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; -using Vintagestory.API.Datastructures; -using Vintagestory.API.Util; - +using Vintagestory.API.Datastructures; +using Vintagestory.API.Util; + namespace Vintagestory.API.Common { - /// - /// A registerable object with variants, i.e. an item, a block or an entity + /// + /// A registerable object with variants, i.e. an item, a block or an entity /// public abstract class RegistryObject { @@ -17,13 +17,13 @@ public abstract class RegistryObject /// public AssetLocation Code = null; - /// - /// Variant values as resolved from blocktype/itemtype. NOT set for entities - use entity.Properties.VariantStrict instead. + /// + /// Variant values as resolved from blocktype/itemtype. NOT set for entities - use entity.Properties.VariantStrict instead. /// public OrderedDictionary VariantStrict = new OrderedDictionary(); - /// - /// Variant values as resolved from blocktype/itemtype. Will not throw an null pointer exception when the key does not exist, but return null instead. NOT set for entities - use entity.Properties.Variant instead + /// + /// Variant values as resolved from blocktype/itemtype. Will not throw an null pointer exception when the key does not exist, but return null instead. NOT set for entities - use entity.Properties.Variant instead /// public RelaxedReadOnlyDictionary Variant; @@ -33,9 +33,9 @@ public abstract class RegistryObject public string Class; - public RegistryObject() - { - Variant = new RelaxedReadOnlyDictionary(VariantStrict); + public RegistryObject() + { + Variant = new RelaxedReadOnlyDictionary(VariantStrict); } /// @@ -104,91 +104,92 @@ public AssetLocation CodeWithParts(params string[] components) AssetLocation newCode = Code.CopyWithPath(CodeWithoutParts(components.Length)); for (int i = 0; i < components.Length; i++) newCode.Path += "-" + components[i]; return newCode; - } - - /// - /// More efficient version of CodeWithParts if there is only a single parameter - /// - /// + } + + /// + /// More efficient version of CodeWithParts if there is only a single parameter + /// + /// public AssetLocation CodeWithParts(string component) { if (Code == null) return null; return Code.CopyWithPath(CodeWithoutParts(1) + "-" + component); - } - - public AssetLocation CodeWithVariant(string type, string value) - { - StringBuilder sb = new StringBuilder(FirstCodePart()); - - foreach (var val in Variant) - { - sb.Append("-"); - - if (val.Key == type) - { - sb.Append(value); - } else - { - sb.Append(val.Value); - } - } - - return new AssetLocation(Code.Domain, sb.ToString()); } + public AssetLocation CodeWithVariant(string type, string value) + { + StringBuilder sb = new StringBuilder(FirstCodePart()); + + foreach (var val in Variant) + { + sb.Append("-"); + + if (val.Key == type) + { + sb.Append(value); + } else + { + sb.Append(val.Value); + } + } + + return new AssetLocation(Code.Domain, sb.ToString()); + } + + + public AssetLocation CodeWithVariants(Dictionary valuesByType) + { + StringBuilder sb = new StringBuilder(FirstCodePart()); + + foreach (var val in Variant) + { + sb.Append("-"); + + string value; - public AssetLocation CodeWithVariants(Dictionary valuesByType) - { - StringBuilder sb = new StringBuilder(FirstCodePart()); - - foreach (var val in Variant) - { - sb.Append("-"); - - string value; - - if (valuesByType.TryGetValue(val.Key, out value)) - { - sb.Append(value); - } - else - { - sb.Append(val.Value); - } - } - - return new AssetLocation(Code.Domain, sb.ToString()); + if (valuesByType.TryGetValue(val.Key, out value)) + { + sb.Append(value); + } + else + { + sb.Append(val.Value); + } + } + + return new AssetLocation(Code.Domain, sb.ToString()); } - public AssetLocation CodeWithVariants(string[] types, string[] values) - { - StringBuilder sb = new StringBuilder(FirstCodePart()); - - foreach (var val in Variant) - { - sb.Append("-"); - - int index = types.IndexOf(val.Key); - - if (index >= 0) - { - sb.Append(values[index]); - } - else - { - sb.Append(val.Value); - } - } - - return new AssetLocation(Code.Domain, sb.ToString()); + public AssetLocation CodeWithVariants(string[] types, string[] values) + { + StringBuilder sb = new StringBuilder(FirstCodePart()); + + foreach (var val in Variant) + { + sb.Append("-"); + + int index = types.IndexOf(val.Key); + + if (index >= 0) + { + sb.Append(values[index]); + } + else + { + sb.Append(val.Value); + } + } + + return new AssetLocation(Code.Domain, sb.ToString()); } /// /// Replaces one part from the blocks code and replaces it with components by splitting it up at every occurence of a dash ('-') /// - /// + /// + /// /// public AssetLocation CodeWithPart(string part, int atPosition = 0) { diff --git a/Common/Text/VtmlParser.cs b/Common/Text/VtmlParser.cs index 472fb848..71492b6e 100644 --- a/Common/Text/VtmlParser.cs +++ b/Common/Text/VtmlParser.cs @@ -430,7 +430,7 @@ public static VtmlToken[] Tokenize(ILogger errorLogger, string vtml) } //
- if (vtml[pos - 1] == '/') + if (pos > 0 && vtml[pos - 1] == '/') { tagToken = parseTagAttributes(tag.Substring(0, tag.Length - 1)); diff --git a/Common/Texture/BitmapExternal.cs b/Common/Texture/BitmapExternal.cs index cceac6d7..db0d5687 100644 --- a/Common/Texture/BitmapExternal.cs +++ b/Common/Texture/BitmapExternal.cs @@ -76,7 +76,7 @@ public BitmapExternal(string filePath) using var canvas = new SKCanvas(bmp); canvas.DrawBitmap(decode, 0, 0); } - catch (Exception ex) + catch (Exception) { bmp = new SKBitmap(1, 1); bmp.SetPixel(0, 0, SKColors.Orange); @@ -96,7 +96,7 @@ public BitmapExternal(Stream stream) using var canvas = new SKCanvas(bmp); canvas.DrawBitmap(decode, 0, 0); } - catch (Exception ex) + catch (Exception) { bmp = new SKBitmap(1, 1); bmp.SetPixel(0, 0, SKColors.Orange); diff --git a/Config/Dimensions.cs b/Config/Dimensions.cs index 30d4c108..b0316096 100644 --- a/Config/Dimensions.cs +++ b/Config/Dimensions.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Vintagestory.API.Config +namespace Vintagestory.API.Config { public class Dimensions { diff --git a/Config/GameVersion.cs b/Config/GameVersion.cs index c1c3b72f..0e362974 100644 --- a/Config/GameVersion.cs +++ b/Config/GameVersion.cs @@ -41,7 +41,7 @@ public static class GameVersion /// /// Version number in the format: major.minor.revision[appendix] /// - public const string ShortGameVersion = OverallVersion + "-pre.6"; + public const string ShortGameVersion = OverallVersion + "-pre.10"; public static EnumReleaseType ReleaseType => GetReleaseType(ShortGameVersion); @@ -61,7 +61,7 @@ public static class GameVersion /// /// Version of the Mod API /// - public const string APIVersion = "1.7.0"; + public const string APIVersion = "1.19.0"; /// /// Version of the Network Protocol diff --git a/Config/GlobalConstants.cs b/Config/GlobalConstants.cs index a44f2059..a82b1ecc 100644 --- a/Config/GlobalConstants.cs +++ b/Config/GlobalConstants.cs @@ -119,9 +119,9 @@ public class GlobalConstants public static float GravityPerSecond = 0.37f; /// - /// Range in blocks at which clients receive regular updates of this entity + /// Range in blocks at where this entity is simulated on the server (MagicNum.cs sets this value) /// - public static int DefaultTrackingRange = 128; + public static int DefaultSimulationRange = 128; /// /// Range in blocks a player can interact with blocks (break, use, place) diff --git a/Config/RuntimeEnv.cs b/Config/RuntimeEnv.cs index 7a9f058e..bb74e86c 100644 --- a/Config/RuntimeEnv.cs +++ b/Config/RuntimeEnv.cs @@ -161,7 +161,7 @@ public static string GetOsString() return $"Linux ({distro}) [Kernel {Environment.OSVersion.Version}]"; } } - catch (Exception _) + catch (Exception) { // ignored } diff --git a/Datastructures/AttributeTree/Other/ScalarAttribute.cs b/Datastructures/AttributeTree/Other/ScalarAttribute.cs index 5f64c87e..10caad75 100644 --- a/Datastructures/AttributeTree/Other/ScalarAttribute.cs +++ b/Datastructures/AttributeTree/Other/ScalarAttribute.cs @@ -12,6 +12,11 @@ public virtual bool Equals(IWorldAccessor worldForResolve, IAttribute attr) return attr.GetValue().Equals(value) || EqualityUtil.NumberEquals(value as object, attr.GetValue()); } + public override bool Equals(object b) + { + return value.Equals(b); + } + public virtual object GetValue() { return value; diff --git a/Datastructures/AttributeTree/TreeAttribute.cs b/Datastructures/AttributeTree/TreeAttribute.cs index 8edfee01..b8746a08 100644 --- a/Datastructures/AttributeTree/TreeAttribute.cs +++ b/Datastructures/AttributeTree/TreeAttribute.cs @@ -815,6 +815,7 @@ IAttribute IAttribute.Clone() /// /// Returns true if given tree contains all of elements of this one, but given tree may contain also more elements. Individual node values are exactly matched. /// + /// /// /// public bool IsSubSetOf(IWorldAccessor worldForResolve, IAttribute other) @@ -844,6 +845,7 @@ public bool IsSubSetOf(IWorldAccessor worldForResolve, IAttribute other) /// /// Returns true if given tree exactly matches this one /// + /// /// /// public bool Equals(IWorldAccessor worldForResolve, IAttribute other) diff --git a/Datastructures/Dictionary/OrderedDictionary.cs b/Datastructures/Dictionary/OrderedDictionary.cs index 5d0620a7..7fcc36c2 100644 --- a/Datastructures/Dictionary/OrderedDictionary.cs +++ b/Datastructures/Dictionary/OrderedDictionary.cs @@ -391,11 +391,12 @@ public bool ContainsValue(TValue value) return Dictionary.ContainsValue(value); } - /// - /// Adds values, but for performance does not replace any existing values which have the same key; that's fine for asset loading - /// - /// - internal void AddRange(Dictionary src, ILogger logger) + /// + /// Adds values, but for performance does not replace any existing values which have the same key; that's fine for asset loading + /// + /// + /// + internal void AddRange(Dictionary src, ILogger logger) { foreach (KeyValuePair val in src) { diff --git a/Datastructures/JsonObject.cs b/Datastructures/JsonObject.cs index b6570d83..7c16bf7e 100644 --- a/Datastructures/JsonObject.cs +++ b/Datastructures/JsonObject.cs @@ -30,7 +30,7 @@ public JsonObject(JToken token) { /// /// Create a new instance of a JsonObject /// - /// + /// /// Only present so that the Constructor with a sole null parameter has an unambiguous signature public JsonObject(JsonObject original, bool unused) { @@ -96,10 +96,10 @@ public T AsObject(T defaultValue, string domain) { JsonSerializerSettings settings = null; - if (domain != GlobalConstants.DefaultDomain) - { - settings = new JsonSerializerSettings(); - settings.Converters.Add(new AssetLocationJsonParser(domain)); + if (domain != GlobalConstants.DefaultDomain) + { + settings = new JsonSerializerSettings(); + settings.Converters.Add(new AssetLocationJsonParser(domain)); } return token == null ? defaultValue : JsonConvert.DeserializeObject(token.ToString(), settings); @@ -107,13 +107,13 @@ public T AsObject(T defaultValue, string domain) public T AsObject(JsonSerializerSettings settings, T defaultValue, string domain = GlobalConstants.DefaultDomain) { - if (domain != GlobalConstants.DefaultDomain) - { - if (settings == null) - { - settings = new JsonSerializerSettings(); - } - settings.Converters.Add(new AssetLocationJsonParser(domain)); + if (domain != GlobalConstants.DefaultDomain) + { + if (settings == null) + { + settings = new JsonSerializerSettings(); + } + settings.Converters.Add(new AssetLocationJsonParser(domain)); } return token == null ? defaultValue : JsonConvert.DeserializeObject(token.ToString(), settings); @@ -165,6 +165,7 @@ public float[] AsFloatArray(float[] defaultValue = null) /// Turn the token into an array /// /// If the conversion fails, this value is used instead + /// /// public T[] AsArray(T[] defaultValue = null, string defaultDomain = null) { diff --git a/Datastructures/LimitedList.cs b/Datastructures/LimitedList.cs index 52662ac6..75f937f0 100644 --- a/Datastructures/LimitedList.cs +++ b/Datastructures/LimitedList.cs @@ -25,6 +25,7 @@ public LimitedList(int maxCapacity) /// Create a new list with a given maximum capacity /// /// + /// public LimitedList(int maxCapacity, IEnumerable initialElements) { if (initialElements == null) diff --git a/Datastructures/SemVer.cs b/Datastructures/SemVer.cs index 15190b6e..151d5a02 100644 --- a/Datastructures/SemVer.cs +++ b/Datastructures/SemVer.cs @@ -95,12 +95,13 @@ public static SemVer Parse(string s) TryParse(s, out var result, true); return result; } - + /// /// Tries to convert the specified string representation of a /// semantic version to its equivalent, /// returning true if successful. /// + /// /// /// When this method returns, contains a valid, non-null SemVer, /// If the conversion failed, this is set to the parser's best guess. @@ -108,12 +109,13 @@ public static SemVer Parse(string s) /// Thrown if the specified string is null. public static bool TryParse(string s, out SemVer result) => TryParse(s, out result, out var _); - + /// /// Tries to convert the specified string representation of a /// semantic version to its equivalent, /// returning true if successful. /// + /// /// /// When this method returns, contains a valid, non-null SemVer, /// If the conversion failed, this is set to the method's best guess. diff --git a/Localization/ITranslationService.cs b/Localization/ITranslationService.cs index e55b5bfb..7b99e4ed 100644 --- a/Localization/ITranslationService.cs +++ b/Localization/ITranslationService.cs @@ -31,6 +31,7 @@ public interface ITranslationService /// Loads only the vanilla JSON files, without dealing with mods, or resource-packs. ///
/// The root assets path to load the vanilla files from. + /// void PreLoad(string assetsPath, bool lazyLoad = false); /// diff --git a/Localization/Lang.cs b/Localization/Lang.cs index c46b813a..703f870f 100644 --- a/Localization/Lang.cs +++ b/Localization/Lang.cs @@ -75,6 +75,8 @@ public static void ChangeLanguage(string languageCode) /// The instance used within the sided API. /// The instance used within the sided API. /// The language code to use as the default language. + /// + /// public static void LoadLanguage(ILogger logger, IAssetManager assetManager, string languageCode = "en", bool lazyLoad = false, EnumLinebreakBehavior lbBehavior = EnumLinebreakBehavior.AfterWord) { if (AvailableLanguages.ContainsKey(languageCode)) @@ -224,6 +226,7 @@ public static IDictionary GetAllEntries() /// /// The key. /// if set to true, the scan will include any wildcarded values. + /// /// true if the specified key has a translation; otherwise, false. public static bool HasTranslation(string key, bool findWildcarded = true, bool logErrors = true) { diff --git a/Localization/TranslationService.cs b/Localization/TranslationService.cs index f907ad2d..b8021081 100644 --- a/Localization/TranslationService.cs +++ b/Localization/TranslationService.cs @@ -42,6 +42,7 @@ public class TranslationService : ITranslationService /// The language code that this translation service caters for. /// The instance used within the sided API. /// The instance used within the sided API. + /// public TranslationService(string languageCode, ILogger logger, IAssetManager assetManager = null, EnumLinebreakBehavior lbBehavior = EnumLinebreakBehavior.AfterWord) { LanguageCode = languageCode; @@ -96,6 +97,7 @@ public void Load(bool lazyLoad = false) /// Loads only the vanilla JSON files, without dealing with mods, or resource-packs. ///
/// The root assets path to load the vanilla files from. + /// public void PreLoad(string assetsPath, bool lazyLoad = false) { preLoadAssetsPath = assetsPath; @@ -162,7 +164,7 @@ private string TryFormat(string value, params object[] args) { result = string.Format(value, args); } - catch (Exception _) + catch (Exception) { result = value; if (logger != null) diff --git a/Math/BlockFacing.cs b/Math/BlockFacing.cs index 6049dfb9..abd55fd4 100644 --- a/Math/BlockFacing.cs +++ b/Math/BlockFacing.cs @@ -257,6 +257,19 @@ public BlockFacing GetCW() return HORIZONTALS_ANGLEORDER[GameMath.Mod(horizontalAngleIndex - 1, 4)]; } + /// + /// Gets the Horizontal BlockFacing by applying the given angel + /// If used on a UP or DOWN BlockFacing it will return it's current BlockFacing + /// + /// + /// + public BlockFacing GetHorizontalRotated(int angle) + { + if (horizontalAngleIndex < 0) return this; + var indexRot = (angle / 90 + index) % 4; + return HORIZONTALS[indexRot]; + } + /// /// Applies a 3d rotation on the face and returns the face thats closest to the rotated face diff --git a/Math/BlockPos.cs b/Math/BlockPos.cs index bf94b3ff..b4174776 100644 --- a/Math/BlockPos.cs +++ b/Math/BlockPos.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.Serialization; using ProtoBuf; using Vintagestory.API.Common; using Vintagestory.API.Config; @@ -30,9 +29,16 @@ public int InternalY { public const int DimensionBoundary = GlobalConstants.DimensionSizeInChunks * GlobalConstants.ChunkSize; + [Obsolete("Not dimension-aware. Use new BlockPos(dimensionId) where possible")] public BlockPos() { } + public BlockPos(int dim) + { + this.dimension = dim; + } + + [Obsolete("Not dimension-aware. Use overload with a dimension parameter instead")] public BlockPos(int x, int y, int z) { @@ -510,11 +516,14 @@ public BlockPos Sub(BlockPos pos) Z -= pos.Z; return this; } - + /// - /// Substract a position => you'll have the manhatten distance + /// Substract a position => you'll have the manhatten distance /// - /// + /// + /// + /// + /// public BlockPos Sub(int x, int y, int z) { X -= x; diff --git a/Math/CollisionTester.cs b/Math/CollisionTester.cs index 77b57024..6c9be43d 100644 --- a/Math/CollisionTester.cs +++ b/Math/CollisionTester.cs @@ -312,15 +312,13 @@ public Cuboidd GetCollidingCollisionBox(IBlockAccessor blockAccessor, Cuboidf en } - - - /// /// Tests given cuboidf collides with the terrain. By default also checks if the cuboid is merely touching the terrain, set alsoCheckTouch to disable that. /// /// /// /// + /// /// /// public bool GetCollidingCollisionBox(IBlockAccessor blockAccessor, Cuboidf entityBoxRel, Vec3d pos, ref Cuboidd intoCubuid, bool alsoCheckTouch = true) diff --git a/Math/ColorUtil.cs b/Math/ColorUtil.cs index 28d5332c..f71968e7 100644 --- a/Math/ColorUtil.cs +++ b/Math/ColorUtil.cs @@ -182,6 +182,7 @@ public static float[] ToRGBAFloats(int color) /// Returns a 4 element rgb float with values between 0..1 /// /// + /// /// public static Vec3f ToRGBVec3f(int color, ref Vec3f outVal) { @@ -274,12 +275,12 @@ public static byte[] ColorMultiply(byte[] color1, byte[] color2) /// public static int ColorMultiplyEach(int color, int color2) { - // yaaaay bracket party (o) )o( o// \o/ - hope you enjoyed the party guys not all brackets were necessary, operator precedence * / before << before | - // could probably save on some bit masking - nope, this bit masking is essential especially when right-shifting signed ints + // yaaaay bracket party (o) )o( o// \o/ - party now tamed a little for readability, surely everyone knows operator precedence * / is left to right and * / both come before << + // At one time it was thought we might be able to save on some bit masking, but nope, this bit masking is essential especially when right-shifting signed ints return - ((color >> 24) & 0xff) * ((color2 >> 24) & 0xff) / 255 << 24 | - ((color >> 16) & 0xff) * ((color2 >> 16) & 0xff) / 255 << 16 | - ((color >> 8) & 0xff) * ((color2 >> 8) & 0xff) / 255 << 8 | + (((color >> 24) & 0xff) * ((color2 >> 24) & 0xff) / 255 << 24) | + (((color >> 16) & 0xff) * ((color2 >> 16) & 0xff) / 255 << 16) | + (((color >> 8) & 0xff) * ((color2 >> 8) & 0xff) / 255 << 8) | (color & 0xff) * (color2 & 0xff) / 255 ; } @@ -318,13 +319,14 @@ public static int ColorMultiply3Clamped(int color, float multiplier) } - - /// /// Multiplies a float value to the rgb color channels /// /// - /// + /// + /// + /// + /// /// public static int ColorMultiply4(int color, float redMul, float greenMul, float blueMul, float alphaMul) { @@ -500,10 +502,10 @@ public static int[] ColorSubstractHSV(int h1, int s1, int v1, int h2, int s2, in /// /// /// - public static int ToRgba(int a, int r, int g, int b) - { - int iCol = (a << 24) | (r << 16) | (g << 8) | b; - return iCol; + public static int ToRgba(int a, int r, int g, int b) + { + int iCol = (a << 24) | (r << 16) | (g << 8) | b; + return iCol; } /// @@ -511,9 +513,9 @@ public static int ToRgba(int a, int r, int g, int b) /// /// /// - public static byte ColorA(int color) - { - return (byte)(color >> 24); + public static byte ColorA(int color) + { + return (byte)(color >> 24); } /// @@ -521,9 +523,9 @@ public static byte ColorA(int color) /// /// /// - public static byte ColorR(int color) - { - return (byte)(color >> 16); + public static byte ColorR(int color) + { + return (byte)(color >> 16); } /// @@ -531,9 +533,9 @@ public static byte ColorR(int color) /// /// /// - public static byte ColorG(int color) - { - return (byte)(color >> 8); + public static byte ColorG(int color) + { + return (byte)(color >> 8); } /// @@ -541,9 +543,9 @@ public static byte ColorG(int color) /// /// /// - public static byte ColorB(int color) - { - return (byte)(color); + public static byte ColorB(int color) + { + return (byte)(color); } /// @@ -828,7 +830,6 @@ public static int GrayscaleColor(byte brightness) /// /// /// - /// /// public static int HsvToRgba(int h, int s, int v) { @@ -842,6 +843,7 @@ public static int HsvToRgba(int h, int s, int v) /// /// /// + /// /// public static int HsvToRgba(int h, int s, int v, int a) { diff --git a/Math/Cuboid/Cuboidd.cs b/Math/Cuboid/Cuboidd.cs index 0d303fb2..c0f7930c 100644 --- a/Math/Cuboid/Cuboidd.cs +++ b/Math/Cuboid/Cuboidd.cs @@ -94,6 +94,17 @@ public Cuboidd Set(Cuboidf selectionBox) return this; } + + public void Set(Cuboidd other) + { + this.X1 = other.X1; + this.Y1 = other.Y1; + this.Z1 = other.Z1; + this.X2 = other.X2; + this.Y2 = other.Y2; + this.Z2 = other.Z2; + } + /// /// Sets the cuboid to the selectionBox, translated by vec /// @@ -556,6 +567,21 @@ public bool Intersects(Cuboidf other, Vec3d offset) return false; } + public bool Intersects(Cuboidf other, double offsetx, double offsety, double offsetz) + { + // For performance, this is a conditional statement with && conjunction: the conditional will fail early if any is false + // We test the X pair first, then the Z pair, then the Y pair, as it's quite easy for any given pair to test false + if (X2 > other.X1 + offsetx && + X1 < other.X2 + offsetx && + Z2 > other.Z1 + offsetz && + Z1 < other.Z2 + offsetz && + Y2 > other.Y1 + offsety && + Y1 < Math.Round(other.Y2 + offsety, 5) // Fix float/double rounding errors. Only need to fix the vertical because gravity. Thankfully we don't have horizontal gravity. + ) return true; + + return false; + } + /// /// If the given cuboid intersects with this cuboid @@ -630,6 +656,5 @@ public bool Equals(Cuboidd other) { return other.X1 == X1 && other.Y1 == Y1 && other.Z1 == Z1 && other.X2 == X2 && other.Y2 == Y2 && other.Z2 == Z2; } - } } diff --git a/Math/Cuboid/Cuboidf.cs b/Math/Cuboid/Cuboidf.cs index 5c05df11..4064c6d9 100644 --- a/Math/Cuboid/Cuboidf.cs +++ b/Math/Cuboid/Cuboidf.cs @@ -109,6 +109,16 @@ public Cuboidf() } + public Cuboidf(double x1, double x2, double y1, double y2, double z1, double z2) + { + X1 = (float)x1; + X2 = (float)x2; + Y1 = (float)y1; + Y2 = (float)y2; + Z1 = (float)z1; + Z2 = (float)z2; + } + public Cuboidf(Vec3f start, Vec3f end) { this.X1 = start.X; diff --git a/Math/Cuboid/Cuboidi.cs b/Math/Cuboid/Cuboidi.cs index 2107b5a2..69f21251 100644 --- a/Math/Cuboid/Cuboidi.cs +++ b/Math/Cuboid/Cuboidi.cs @@ -26,91 +26,34 @@ public class Cuboidi : ICuboid, IEquatable public int[] Coordinates => new int[] { X1, Y1, Z1, X2, Y2, Z2 }; - public int MinX - { - get { return Math.Min(X1, X2); } - } - public int MinY - { - get { return Math.Min(Y1, Y2); } - } - public int MinZ - { - get { return Math.Min(Z1, Z2); } - } + public int MinX => Math.Min(X1, X2); + public int MinY => Math.Min(Y1, Y2); + public int MinZ => Math.Min(Z1, Z2); - public int MaxX - { - get { return Math.Max(X1, X2); } - } - public int MaxY - { - get { return Math.Max(Y1, Y2); } - } - public int MaxZ - { - get { return Math.Max(Z1, Z2); } - } + public int MaxX => Math.Max(X1, X2); + public int MaxY => Math.Max(Y1, Y2); + public int MaxZ => Math.Max(Z1, Z2); + public int SizeX => MaxX - MinX; + public int SizeY => MaxY - MinY; + public int SizeZ => MaxZ - MinZ; - public int SizeX - { - get { return MaxX - MinX; } - } + public int SizeXYZ => SizeX * SizeY * SizeZ; + public int SizeXZ => SizeX * SizeZ; - public int SizeY - { - get { return MaxY - MinY; } - } + public Vec3i Start => new Vec3i(X1, Y1, Z1); + public Vec3i End => new Vec3i(X2, Y2, Z2); + public Vec3i Center => new Vec3i((X1 + X2) / 2, (Y1 + Y2) / 2, (Z1 + Z2) / 2); - public int SizeZ - { - get { return MaxZ - MinZ; } - } + public int CenterX => (X1 + X2) / 2; + public int CenterY => (Y1 + Y2) / 2; + public int CenterZ => (Z1 + Z2) / 2; + public int Volume => SizeX * SizeY * SizeZ; - public int SizeXYZ - { - get { return SizeX * SizeY * SizeZ; } - } - public int SizeXZ - { - get { return SizeX * SizeZ; } - } - - - public Vec3i Start - { - get { return new Vec3i(X1, Y1, Z1); } - } - - public Vec3i End - { - get { return new Vec3i(X2, Y2, Z2); } - } - - public Vec3i Center - { - get - { - return new Vec3i((X1 + X2) / 2, (Y1 + Y2) / 2, (Z1 + Z2) / 2); - } - } - - public int Volume - { - get - { - return SizeX * SizeY * SizeZ; - } - } - - public Cuboidi() - { - - } + public Cuboidi() { } public Cuboidi(int[] coordinates) { diff --git a/Math/Noise/NewNormalizedSimplexFractalNoise.cs b/Math/Noise/NewNormalizedSimplexFractalNoise.cs index f0e0f569..470743da 100644 --- a/Math/Noise/NewNormalizedSimplexFractalNoise.cs +++ b/Math/Noise/NewNormalizedSimplexFractalNoise.cs @@ -212,11 +212,11 @@ public double NoiseSign(double y, double inverseCurvedThresholder) double valueTempMax = inverseCurvedThresholder; for (int j = 0; j < orderedOctaveEntries.Length; j++) { - ref readonly OctaveEntry octaveEntry = ref orderedOctaveEntries[j]; - // Exit if we couldn't possibly trigger an early return within this loop. if (!(valueTempMax <= 0) && !(valueTempMin >= 0)) break; + ref readonly OctaveEntry octaveEntry = ref orderedOctaveEntries[j]; + // Stop if no further noise calculation is necessary to know the sign of the result. if (valueTempMin >= octaveEntry.StopBound) return valueTempMin; if (valueTempMax <= -octaveEntry.StopBound) return valueTempMax; diff --git a/Math/Noise/NewSimplexNoiseLayer.cs b/Math/Noise/NewSimplexNoiseLayer.cs index 71b1cc18..3a4adc9c 100644 --- a/Math/Noise/NewSimplexNoiseLayer.cs +++ b/Math/Noise/NewSimplexNoiseLayer.cs @@ -75,8 +75,9 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float y1 = yi - 0.5f; float z1 = zi - 0.5f; float a1 = 0.75f - x1 * x1 - y1 * y1 - z1 * z1; - long hash1 = HashPrimes(seed2, xrbp + PrimeX, yrbp + PrimeY, zrbp + PrimeZ); - value += (a1 * a1) * (a1 * a1) * Grad(hash1, x1, y1, z1); + value += (a1 * a1) * (a1 * a1) * Grad( + HashPrimes(seed2, xrbp + PrimeX, yrbp + PrimeY, zrbp + PrimeZ), + x1, y1, z1); // Shortcuts for building the remaining falloffs. // Derived by subtracting the polynomials with the offsets plugged in. @@ -91,32 +92,35 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float a2 = xAFlipMask0 + a0; if (a2 > 0) { - float x2 = x0 - (xNMask | 1); - float y2 = y0; - float z2 = z0; - long hash2 = HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (zNMask & PrimeZ)); - value += (a2 * a2) * (a2 * a2) * Grad(hash2, x2, y2, z2); + value += (a2 * a2) * (a2 * a2) * Grad( + HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (zNMask & PrimeZ)), + x0 - (xNMask | 1), + y0, + z0 + ); } else { float a3 = yAFlipMask0 + zAFlipMask0 + a0; if (a3 > 0) { - float x3 = x0; - float y3 = y0 - (yNMask | 1); - float z3 = z0 - (zNMask | 1); - long hash3 = HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)); - value += (a3 * a3) * (a3 * a3) * Grad(hash3, x3, y3, z3); + value += (a3 * a3) * (a3 * a3) * Grad( + HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)), + x0, + y0 - (yNMask | 1), + z0 - (zNMask | 1) + ); } float a4 = xAFlipMask1 + a1; if (a4 > 0) { - float x4 = (xNMask | 1) + x1; - float y4 = y1; - float z4 = z1; - long hash4 = HashPrimes(seed2, xrbp + (xNMask & unchecked(PrimeX * 2)), yrbp + PrimeY, zrbp + PrimeZ); - value += (a4 * a4) * (a4 * a4) * Grad(hash4, x4, y4, z4); + value += (a4 * a4) * (a4 * a4) * Grad( + HashPrimes(seed2, xrbp + (xNMask & unchecked(PrimeX * 2)), yrbp + PrimeY, zrbp + PrimeZ), + (xNMask | 1) + x1, + y1, + z1 + ); skip5 = true; } } @@ -125,32 +129,35 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float a6 = yAFlipMask0 + a0; if (a6 > 0) { - float x6 = x0; - float y6 = y0 - (yNMask | 1); - float z6 = z0; - long hash6 = HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (zNMask & PrimeZ)); - value += (a6 * a6) * (a6 * a6) * Grad(hash6, x6, y6, z6); + value += (a6 * a6) * (a6 * a6) * Grad( + HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (zNMask & PrimeZ)), + x0, + y0 - (yNMask | 1), + z0 + ); } else { float a7 = xAFlipMask0 + zAFlipMask0 + a0; if (a7 > 0) { - float x7 = x0 - (xNMask | 1); - float y7 = y0; - float z7 = z0 - (zNMask | 1); - long hash7 = HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)); - value += (a7 * a7) * (a7 * a7) * Grad(hash7, x7, y7, z7); + value += (a7 * a7) * (a7 * a7) * Grad( + HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)), + x0 - (xNMask | 1), + y0, + z0 - (zNMask | 1) + ); } float a8 = yAFlipMask1 + a1; if (a8 > 0) { - float x8 = x1; - float y8 = (yNMask | 1) + y1; - float z8 = z1; - long hash8 = HashPrimes(seed2, xrbp + PrimeX, yrbp + (yNMask & (PrimeY << 1)), zrbp + PrimeZ); - value += (a8 * a8) * (a8 * a8) * Grad(hash8, x8, y8, z8); + value += (a8 * a8) * (a8 * a8) * Grad( + HashPrimes(seed2, xrbp + PrimeX, yrbp + (yNMask & (PrimeY << 1)), zrbp + PrimeZ), + x1, + (yNMask | 1) + y1, + z1 + ); skip9 = true; } } @@ -159,32 +166,35 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float aA = zAFlipMask0 + a0; if (aA > 0) { - float xA = x0; - float yA = y0; - float zA = z0 - (zNMask | 1); - long hashA = HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)); - value += (aA * aA) * (aA * aA) * Grad(hashA, xA, yA, zA); + value += (aA * aA) * (aA * aA) * Grad( + HashPrimes(seed, xrbp + (xNMask & PrimeX), yrbp + (yNMask & PrimeY), zrbp + (~zNMask & PrimeZ)), + x0, + y0, + z0 - (zNMask | 1) + ); } else { float aB = xAFlipMask0 + yAFlipMask0 + a0; if (aB > 0) { - float xB = x0 - (xNMask | 1); - float yB = y0 - (yNMask | 1); - float zB = z0; - long hashB = HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (zNMask & PrimeZ)); - value += (aB * aB) * (aB * aB) * Grad(hashB, xB, yB, zB); + value += (aB * aB) * (aB * aB) * Grad( + HashPrimes(seed, xrbp + (~xNMask & PrimeX), yrbp + (~yNMask & PrimeY), zrbp + (zNMask & PrimeZ)), + x0 - (xNMask | 1), + y0 - (yNMask | 1), + z0 + ); } float aC = zAFlipMask1 + a1; if (aC > 0) { - float xC = x1; - float yC = y1; - float zC = (zNMask | 1) + z1; - long hashC = HashPrimes(seed2, xrbp + PrimeX, yrbp + PrimeY, zrbp + (zNMask & (PrimeZ << 1))); - value += (aC * aC) * (aC * aC) * Grad(hashC, xC, yC, zC); + value += (aC * aC) * (aC * aC) * Grad( + HashPrimes(seed2, xrbp + PrimeX, yrbp + PrimeY, zrbp + (zNMask & (PrimeZ << 1))), + x1, + y1, + (zNMask | 1) + z1 + ); skipD = true; } } @@ -194,11 +204,12 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float a5 = yAFlipMask1 + zAFlipMask1 + a1; if (a5 > 0) { - float x5 = x1; - float y5 = (yNMask | 1) + y1; - float z5 = (zNMask | 1) + z1; - long hash5 = HashPrimes(seed2, xrbp + PrimeX, yrbp + (yNMask & (PrimeY << 1)), zrbp + (zNMask & (PrimeZ << 1))); - value += (a5 * a5) * (a5 * a5) * Grad(hash5, x5, y5, z5); + value += (a5 * a5) * (a5 * a5) * Grad( + HashPrimes(seed2, xrbp + PrimeX, yrbp + (yNMask & (PrimeY << 1)), zrbp + (zNMask & (PrimeZ << 1))), + x1, + (yNMask | 1) + y1, + (zNMask | 1) + z1 + ); } } @@ -207,11 +218,12 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float a9 = xAFlipMask1 + zAFlipMask1 + a1; if (a9 > 0) { - float x9 = (xNMask | 1) + x1; - float y9 = y1; - float z9 = (zNMask | 1) + z1; - long hash9 = HashPrimes(seed2, xrbp + (xNMask & unchecked(PrimeX * 2)), yrbp + PrimeY, zrbp + (zNMask & (PrimeZ << 1))); - value += (a9 * a9) * (a9 * a9) * Grad(hash9, x9, y9, z9); + value += (a9 * a9) * (a9 * a9) * Grad( + HashPrimes(seed2, xrbp + (xNMask & unchecked(PrimeX * 2)), yrbp + PrimeY, zrbp + (zNMask & (PrimeZ << 1))), + (xNMask | 1) + x1, + y1, + (zNMask | 1) + z1 + ); } } @@ -220,11 +232,12 @@ private static float Noise3_UnrotatedBase(long seed, double xr, double yr, doubl float aD = xAFlipMask1 + yAFlipMask1 + a1; if (aD > 0) { - float xD = (xNMask | 1) + x1; - float yD = (yNMask | 1) + y1; - float zD = z1; - long hashD = HashPrimes(seed2, xrbp + (xNMask & (PrimeX << 1)), yrbp + (yNMask & (PrimeY << 1)), zrbp + PrimeZ); - value += (aD * aD) * (aD * aD) * Grad(hashD, xD, yD, zD); + value += (aD * aD) * (aD * aD) * Grad( + HashPrimes(seed2, xrbp + (xNMask & (PrimeX << 1)), yrbp + (yNMask & (PrimeY << 1)), zrbp + PrimeZ), + (xNMask | 1) + x1, + (yNMask | 1) + y1, + z1 + ); } } diff --git a/Math/Noise/NormalizedSimplexNoise.cs b/Math/Noise/NormalizedSimplexNoise.cs index 90f2a60b..ef2ee87c 100644 --- a/Math/Noise/NormalizedSimplexNoise.cs +++ b/Math/Noise/NormalizedSimplexNoise.cs @@ -189,9 +189,7 @@ public static double NoiseValueCurve(double value) public static double NoiseValueCurveInverse(double value) { if (value <= 0.0) return double.NegativeInfinity; if (value >= 1.0) return double.PositiveInfinity; - value *= 2.0; - value /= (2.0 - value); - return 0.5 * Math.Log(value); + return 0.5 * Math.Log(value / (1.0 - value)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Math/Vector/Vec3iAndFacingFlags.cs b/Math/Vector/Vec3iAndFacingFlags.cs index c73dc974..3ba135a6 100644 --- a/Math/Vector/Vec3iAndFacingFlags.cs +++ b/Math/Vector/Vec3iAndFacingFlags.cs @@ -9,6 +9,8 @@ public class Vec3iAndFacingFlags public int FacingFlags; public int OppositeFlags; public int extIndexOffset; + public int OppositeFlagsUpperOrLeft; + public int OppositeFlagsLowerOrRight; public static void Initialize(int value) { @@ -23,6 +25,20 @@ public Vec3iAndFacingFlags(int x, int y, int z, int flags, int oppositeFlags) FacingFlags = flags; OppositeFlags = oppositeFlags; extIndexOffset = MapUtil.Index3d(x, y, z, extChunkSize, extChunkSize); + OppositeFlagsUpperOrLeft = oppositeFlags; + OppositeFlagsLowerOrRight = oppositeFlags; + } + + public Vec3iAndFacingFlags(int x, int y, int z, int flags, int oppositeFlags, int flagsUL, int flagsLR) + { + X = x; + Y = y; + Z = z; + FacingFlags = flags; + OppositeFlags = oppositeFlags; + extIndexOffset = MapUtil.Index3d(x, y, z, extChunkSize, extChunkSize); + OppositeFlagsUpperOrLeft = flagsUL; + OppositeFlagsLowerOrRight = flagsLR; } } } diff --git a/Server/API/IPlayerDataManager.cs b/Server/API/IPlayerDataManager.cs index a26d2b49..d3e78027 100644 --- a/Server/API/IPlayerDataManager.cs +++ b/Server/API/IPlayerDataManager.cs @@ -42,7 +42,7 @@ public interface IPlayerDataManager /// /// Resolves a player uid to a player name, independent on whether this player is online, offline or never even joined the server. This is done by contacting the auth server, so please use this method sparingly. /// - /// + /// /// void ResolvePlayerUid(string playeruid, Action onPlayerReceived); diff --git a/Server/API/IServerNetworkChannel.cs b/Server/API/IServerNetworkChannel.cs index 5775d377..892a1224 100644 --- a/Server/API/IServerNetworkChannel.cs +++ b/Server/API/IServerNetworkChannel.cs @@ -13,7 +13,7 @@ public interface INetworkMessage { }; /// Handler for processing a message /// /// - /// + /// public delegate void NetworkClientMessageHandler(IServerPlayer fromPlayer, T packet); diff --git a/Server/API/IServerWorldAccessor.cs b/Server/API/IServerWorldAccessor.cs index b4daa476..6b57eafb 100644 --- a/Server/API/IServerWorldAccessor.cs +++ b/Server/API/IServerWorldAccessor.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Collections.Generic; using Vintagestory.API.Common; using Vintagestory.API.Common.Entities; using Vintagestory.API.Datastructures; @@ -32,6 +33,7 @@ public interface IServerWorldAccessor : IWorldAccessor /// /// /// + /// void CreateExplosion(BlockPos pos, EnumBlastType blastType, double destructionRadius, double injureRadius, float blockDropChanceMultiplier = 1); /// @@ -39,6 +41,11 @@ public interface IServerWorldAccessor : IWorldAccessor /// OrderedDictionary TreeGenerators { get; } + /// + /// Dictionary of entity codes to remap, old codes (which may be present in save files or schematics) as keys + /// + Dictionary RemappedEntities { get; } + bool IsFullyLoadedChunk(BlockPos pos); /// diff --git a/Server/API/IWorldManagerAPI.cs b/Server/API/IWorldManagerAPI.cs index eca249f9..77dc26ef 100644 --- a/Server/API/IWorldManagerAPI.cs +++ b/Server/API/IWorldManagerAPI.cs @@ -247,7 +247,9 @@ public interface IWorldManagerAPI /// Asynchrounly checks if this chunk is currently loaded or in the savegame database. Calls the callback method with true or false once done looking up. Does not load the actual chunk data. /// /// + /// /// + /// /// void TestChunkExists(int chunkX, int chunkY, int chunkZ, Action onTested); @@ -262,8 +264,8 @@ public interface IWorldManagerAPI /// /// Asynchrounly checks if this mapregion is currently loaded or in the savegame database. Calls the callback method with true or false once done looking up. Does not load the actual map region data. /// - /// - /// + /// + /// /// void TestMapRegionExists(int regionX, int regionZ, Action onTested); @@ -292,6 +294,7 @@ public interface IWorldManagerAPI /// /// /// + /// /// If true, the chunk will not be sent to connected players that are out of range from that chunk void SendChunk(int chunkX, int chunkY, int chunkZ, IServerPlayer player, bool onlyIfInRange = true); @@ -388,12 +391,12 @@ public interface IWorldManagerAPI /// Default spawnpoint int[] DefaultSpawnPosition { get; } - /// - /// Permanently sets the default spawnpoint - /// - /// X coordinate of new spawnpoint - /// Y coordinate of new spawnpoint - /// Z coordinate of new spawnpoint + /// + /// Permanently sets the default spawnpoint + /// + /// X coordinate of new spawnpoint + /// Y coordinate of new spawnpoint + /// Z coordinate of new spawnpoint void SetDefaultSpawnPosition(int x, int y, int z); @@ -403,11 +406,11 @@ public interface IWorldManagerAPI #region Get/Set Block Infos - /// - /// Get the ID of a certain BlockType - /// - /// Name of the BlockType - /// ID of the BlockType + /// + /// Get the ID of a certain BlockType + /// + /// Name of the BlockType + /// ID of the BlockType int GetBlockId(AssetLocation name); diff --git a/Server/IServerConfig.cs b/Server/IServerConfig.cs index 974b033b..c97ff92d 100644 --- a/Server/IServerConfig.cs +++ b/Server/IServerConfig.cs @@ -94,7 +94,7 @@ public interface IServerConfig bool AllowFireSpread { get; set; } /// - /// Whether or not falling blocks should fall (e.g. sand and gravel) + /// Whether or not falling blocks should fall (e.g. sand and gravel) and floating single rock blocks should break /// bool AllowFallingBlocks { get; set; } diff --git a/Server/Worldgen/INeighbourChunkProvider.cs b/Server/Worldgen/INeighbourChunkProvider.cs index 1e0b822c..f25f6bcb 100644 --- a/Server/Worldgen/INeighbourChunkProvider.cs +++ b/Server/Worldgen/INeighbourChunkProvider.cs @@ -17,6 +17,7 @@ public interface IChunkProvider /// /// /// + /// /// IWorldChunk GetUnpackedChunkFast(int chunkX, int chunkY, int chunkZ, bool notRecentlyAccessed = false); diff --git a/Util/NetUtil.cs b/Util/NetUtil.cs index 74b29f83..85253946 100644 --- a/Util/NetUtil.cs +++ b/Util/NetUtil.cs @@ -19,7 +19,7 @@ public static void OpenUrlInBrowser(string url) { try { - Process.Start(url); + Process.Start("start \"" + url + "\""); } catch { diff --git a/Util/StringUtil.cs b/Util/StringUtil.cs index 71db5f37..88389562 100644 --- a/Util/StringUtil.cs +++ b/Util/StringUtil.cs @@ -107,6 +107,7 @@ public static int CountChars(this string text, char c) return cnt; } + public static bool ContainsFast(this string value, string reference) { if (reference.Length > value.Length) return false; @@ -123,6 +124,16 @@ public static bool ContainsFast(this string value, string reference) return false; } + public static bool ContainsFast(this string value, char reference) + { + for (int i = 0; i < value.Length; i++) + { + if (value[i] == reference) return true; + } + + return false; + } + public static bool StartsWithFast(this string value, string reference) { diff --git a/Util/WildcardUtil.cs b/Util/WildcardUtil.cs index 83648dad..0961881f 100644 --- a/Util/WildcardUtil.cs +++ b/Util/WildcardUtil.cs @@ -10,6 +10,7 @@ public static class WildcardUtil /// Returns a new AssetLocation with the wildcards (*) being filled with the blocks other Code parts, if the wildcard matches. /// Example this block is trapdoor-up-north. search is *-up-*, replace is *-down-*, in this case this method will return trapdoor-down-north. /// + /// /// /// /// @@ -140,9 +141,9 @@ private static bool fastMatch(string needle, string haystack) if (needle[0] == '@') { return Regex.IsMatch(haystack, @"^" + needle.Substring(1) + @"$", RegexOptions.None); - } - - int needleLength = needle.Length; + } + + int needleLength = needle.Length; for (int i = 0; i < needleLength; i++) { char ch = needle[i]; @@ -184,9 +185,9 @@ private static bool fastMatch(string needle, string haystack) char h = haystack[i]; if (ch != h && char.ToLowerInvariant(ch) != char.ToLowerInvariant(h)) return false; } - } - - // No * wildcard? Then we're good if needle is of equal length than haystack + } + + // No * wildcard? Then we're good if needle is of equal length than haystack return needle.Length == haystack.Length; } diff --git a/VintagestoryAPI.csproj b/VintagestoryAPI.csproj index b5dbc590..0b11c5b7 100644 --- a/VintagestoryAPI.csproj +++ b/VintagestoryAPI.csproj @@ -4,10 +4,8 @@ False net7.0 ..\bin\$(Configuration) - false Debug;Release;PerfTest True - 1701;1702;1591;1572;1573;0618 @@ -28,7 +26,7 @@ - + diff --git a/index.md b/index.md index 2ecd6075..22470e20 100644 --- a/index.md +++ b/index.md @@ -4,5 +4,5 @@ ## This is just a reference doc If you want to learn how to mod the game, check out the tutorials on the [Official Vintage Story Wiki](http://wiki.vintagestory.at/index.php?title=Main_Page), this site merely acts as a reference documentation to look stuff up on -Reference doc for game version 1.19.0-pre.6 +Reference doc for game version 1.19.0-pre.10