diff --git a/Code/Entities/ThrowBox.cs b/Code/Entities/ThrowBox.cs index 0153560..ccdff83 100644 --- a/Code/Entities/ThrowBox.cs +++ b/Code/Entities/ThrowBox.cs @@ -39,6 +39,9 @@ public class ThrowBox : Actor { private readonly string _levelName; private readonly bool _tutorial; private readonly TransitionListener _transitionListener; + private readonly bool _canPassThroughSpinners; + private readonly ParticleType _p_Impact; + private readonly char _debrisTypeOverride; private Vector2 _prevLiftSpeed; private Level _level; @@ -55,7 +58,8 @@ public class ThrowBox : Actor { private BirdTutorialGui _tutorialPutDown; private bool _isCrucial; - public ThrowBox(Vector2 position, bool isMetal, bool tutorial = false, bool isSpecial = false, bool isCrucial = false) + public ThrowBox(Vector2 position, bool isMetal, bool tutorial = false, bool isSpecial = false, bool isCrucial = false, bool canPassThroughSpinners = false, + string crateTextureOverride = null, string crucialTextureOverride = null, ParticleType impactParticlesOverride = null, char debrisTypeOverride = '\0') : base(position) { Position -= DISPLACEMENT; _starterPosition = Position; @@ -65,13 +69,21 @@ public ThrowBox(Vector2 position, bool isMetal, bool tutorial = false, bool isSp IsSpecial = isSpecial; _isCrucial = isCrucial; _tutorial = tutorial; - string pathString = isMetal ? "crate_metal0" : "crate0"; + _canPassThroughSpinners = canPassThroughSpinners; - Add(_image = new Image(GFX.Game[$"objects/FactoryHelper/crate/{pathString}"])); + string crateTexture; + if (string.IsNullOrEmpty(crateTextureOverride)) { + string pathString = isMetal ? "crate_metal0" : "crate0"; + crateTexture = $"objects/FactoryHelper/crate/{pathString}"; + } else { + crateTexture = crateTextureOverride; + } + Add(_image = new Image(GFX.Game[crateTexture])); _image.Position += DISPLACEMENT; if (_isCrucial) { - Add(_warningImage = new Image(GFX.Game["objects/FactoryHelper/crate/crucial"])); + string crucialTexture = string.IsNullOrEmpty(crucialTextureOverride) ? "objects/FactoryHelper/crate/crucial" : crucialTextureOverride; + Add(_warningImage = new Image(GFX.Game[crucialTexture])); _warningImage.Position += DISPLACEMENT; } @@ -98,19 +110,42 @@ public ThrowBox(Vector2 position, bool isMetal, bool tutorial = false, bool isSp Add(new LightOcclude(0.2f)); Add(new MirrorReflection()); + + _p_Impact = impactParticlesOverride ?? P_Impact; + _debrisTypeOverride = debrisTypeOverride; } public ThrowBox(EntityData data, Vector2 offset) - : this(data.Position + offset, data.Bool("isMetal", false), data.Bool("tutorial", false), data.Bool("isSpecial", false), data.Bool("isCrucial", false)) { + : this(data.Position + offset, data.Bool("isMetal", false), data.Bool("tutorial", false), data.Bool("isSpecial", false), data.Bool("isCrucial", false), data.Bool("canPassThroughSpinners", false), + GetCrateTextureOverride(data), GetCrucialTextureOverride(data), GetImpactParticlesOverride(data), GetDebrisTypeOverride(data)) { _levelName = data.Level.Name; } + private static string GetCrateTextureOverride(EntityData data) { + return data.Bool("overrideTextures", false) ? data.Attr("crateTexturePath") : null; + } + + private static string GetCrucialTextureOverride(EntityData data) { + return data.Bool("overrideTextures", false) ? data.Attr("crucialTexturePath") : null; + } + + private static ParticleType GetImpactParticlesOverride(EntityData data) { + return data.Bool("overrideParticles", false) ? new ParticleType(P_Impact) { + Color = data.HexColor("impactParticlesColor") + } : null; + } + + private static char GetDebrisTypeOverride(EntityData data) { + return data.Bool("overrideDebris", false) ? data.Char("debrisFromTiletype") : '\0'; + } + private void OnSteamWall(SteamWall steamWall) { Shatter(); } private void OnHitSpinner(Entity spinner) { - Shatter(); + if (!_canPassThroughSpinners) + Shatter(); } public override void Added(Scene scene) { @@ -419,7 +454,7 @@ private void ImpactParticles(Vector2 dir) { positionRange = Vector2.UnitX * 6f; } - (Scene as Level).Particles.Emit(P_Impact, 12, position, positionRange, direction); + (Scene as Level).Particles.Emit(_p_Impact, 12, position, positionRange, direction); } private void Shatter() { @@ -434,7 +469,9 @@ private void Shatter() { for (int i = 0; i < Width / 8f; i++) { for (int j = 0; j < Height / 8f; j++) { - if (_isMetal) { + if (_debrisTypeOverride != '\0') { + Scene.Add(Engine.Pooler.Create().Init(Position + new Vector2(4 + (i * 8), 4 + (j * 8)) + DISPLACEMENT, _debrisTypeOverride, false).BlastFrom(Center)); + } else if (_isMetal) { Scene.Add(Engine.Pooler.Create().Init(Position + new Vector2(4 + (i * 8), 4 + (j * 8)) + DISPLACEMENT, '8', false).BlastFrom(Center)); } else { Scene.Add(Engine.Pooler.Create().Init(Position + new Vector2(4 + (i * 8), 4 + (j * 8)) + DISPLACEMENT, '9', false).BlastFrom(Center)); diff --git a/Code/Entities/ThrowBoxSpawner.cs b/Code/Entities/ThrowBoxSpawner.cs index a8003d8..0bad080 100644 --- a/Code/Entities/ThrowBoxSpawner.cs +++ b/Code/Entities/ThrowBoxSpawner.cs @@ -19,8 +19,17 @@ public class ThrowBoxSpawner : Entity { private readonly HashSet _boxes = new(); private readonly bool _fromTop; private readonly bool _tutorial; + private readonly bool _canPassThroughSpinners; - public ThrowBoxSpawner(Vector2 position, float delay, int maximum, string activationId, bool isMetal, bool isRandom, bool fromTop, bool tutorial, bool startActive) : base(position) { + private readonly string _woodenCrateTextureOverride; + private readonly string _metalCrateTextureOverride; + private readonly ParticleType _impactParticlesOverride; + private readonly char _woodenDebrisTypeOverride; + private readonly char _metalDebrisTypeOverride; + + public ThrowBoxSpawner(Vector2 position, float delay, int maximum, string activationId, bool isMetal, bool isRandom, bool fromTop, bool tutorial, bool startActive, bool canPassThroughSpinners = false, + string woodenCrateTextureOverride = null, string metalCrateTextureOverride = null, ParticleType impactParticlesOverride = null, char woodenDebrisTypeOverride = '\0', char metalDebrisTypeOverride = '\0') + : base(position) { Add(Activator = new FactoryActivator()); Activator.ActivationId = activationId == string.Empty ? null : activationId; Activator.StartOn = startActive; @@ -33,13 +42,44 @@ public ThrowBoxSpawner(Vector2 position, float delay, int maximum, string activa _isRandom = isRandom; _fromTop = fromTop; _tutorial = tutorial; + _canPassThroughSpinners = canPassThroughSpinners; + + _woodenCrateTextureOverride = woodenCrateTextureOverride; + _metalCrateTextureOverride = metalCrateTextureOverride; + _impactParticlesOverride = impactParticlesOverride; + _woodenDebrisTypeOverride = woodenDebrisTypeOverride; + _metalDebrisTypeOverride = metalDebrisTypeOverride; + Add(new Coroutine(SpawnSequence())); Add(new SteamCollider(OnSteamWall)); } public ThrowBoxSpawner(EntityData data, Vector2 offset) : this(data.Position + offset, data.Float("delay", 5f), data.Int("maximum", 0), data.Attr("activationId"), data.Bool("isMetal", false), - data.Bool("isRandom", false), data.Bool("fromTop", true), data.Bool("tutorial", false), data.Bool("startActive", true)) { + data.Bool("isRandom", false), data.Bool("fromTop", true), data.Bool("tutorial", false), data.Bool("startActive", true), data.Bool("canPassThroughSpinners", false), + GetWoodenCrateTextureOverride(data), GetMetalCrateTextureOverride(data), GetImpactParticlesOverride(data), GetWoodenDebrisTypeOverride(data), GetMetalDebrisTypeOverride(data)) { + } + + private static string GetWoodenCrateTextureOverride(EntityData data) { + return data.Bool("overrideTextures", false) ? data.Attr("woodenCrateTexturePath") : null; + } + + private static string GetMetalCrateTextureOverride(EntityData data) { + return data.Bool("overrideTextures", false) ? data.Attr("metalCrateTexturePath") : null; + } + + private static ParticleType GetImpactParticlesOverride(EntityData data) { + return data.Bool("overrideParticles", false) ? new ParticleType(ThrowBox.P_Impact) { + Color = data.HexColor("impactParticlesColor") + } : null; + } + + private static char GetWoodenDebrisTypeOverride(EntityData data) { + return data.Bool("overrideDebris", false) ? data.Char("woodenDebrisFromTiletype") : '\0'; + } + + private static char GetMetalDebrisTypeOverride(EntityData data) { + return data.Bool("overrideDebris", false) ? data.Char("metalDebrisFromTiletype") : '\0'; } private void OnSteamWall(SteamWall steamWall) { @@ -66,10 +106,24 @@ private void TrySpawnThrowBox() { if (_maximum <= 0 || _boxes.Count < _maximum) { float posY = _fromTop ? SceneAs().Bounds.Top - 15 : Position.Y; float posX = _fromTop ? Position.X : GetClosestPositionH(); + bool isMetal = _isRandom ? Calc.Random.Chance(0.5f) : _isMetal; + string crateTextureOverride; + char debrisTypeOverride; + if (isMetal) { + crateTextureOverride = _metalCrateTextureOverride; + debrisTypeOverride = _metalDebrisTypeOverride; + } else { + crateTextureOverride = _woodenCrateTextureOverride; + debrisTypeOverride = _woodenDebrisTypeOverride; + } ThrowBox crate = new( position: new Vector2(posX, posY), - isMetal: _isRandom ? Calc.Random.Chance(0.5f) : _isMetal, - tutorial: _tutorial + isMetal: isMetal, + tutorial: _tutorial, + canPassThroughSpinners: _canPassThroughSpinners, + crateTextureOverride: crateTextureOverride, + impactParticlesOverride: _impactParticlesOverride, + debrisTypeOverride: debrisTypeOverride ); Scene.Add(crate); _boxes.Add(crate); diff --git a/Code/FactoryHelperHooks.cs b/Code/FactoryHelperHooks.cs index 34ba0ae..06f9be6 100644 --- a/Code/FactoryHelperHooks.cs +++ b/Code/FactoryHelperHooks.cs @@ -20,6 +20,18 @@ public static void Load() { On.Celeste.DashBlock.RemoveAndFlagAsGone += DashBlockRemoveAndFlagAsGone; } + public static void Unload() { + On.Celeste.Player.ctor -= ctor; + On.Celeste.Level.LoadLevel -= LoadLevel; + On.Celeste.Player.Die -= PlayerDie; + On.Celeste.LevelExit.Routine -= RespawnRoutine; + On.Celeste.Player.Pickup -= Pickup; + On.Celeste.Lookout.LookRoutine -= LookRoutine; + On.Celeste.LevelEnter.Go -= LevelEnterGo; + On.Celeste.DashBlock.Break_Vector2_Vector2_bool_bool -= DashBlockBreak; + On.Celeste.DashBlock.RemoveAndFlagAsGone -= DashBlockRemoveAndFlagAsGone; + } + private static void DashBlockRemoveAndFlagAsGone(On.Celeste.DashBlock.orig_RemoveAndFlagAsGone orig, DashBlock self) { if (self is FactoryActivatorDashBlock) { self.RemoveSelf(); diff --git a/Code/FactoryHelperModule.cs b/Code/FactoryHelperModule.cs index b1dca14..059292f 100644 --- a/Code/FactoryHelperModule.cs +++ b/Code/FactoryHelperModule.cs @@ -28,6 +28,8 @@ public override void Load() { FactoryHelperHooks.Load(); } - public override void Unload() { } + public override void Unload() { + FactoryHelperHooks.Unload(); + } } } diff --git a/Loenn/entities/crate.lua b/Loenn/entities/crate.lua index 540bd30..0227fb6 100644 --- a/Loenn/entities/crate.lua +++ b/Loenn/entities/crate.lua @@ -1,7 +1,22 @@ +local drawableSprite = require("structs.drawable_sprite") +local fakeTilesHelper = require("helpers.fake_tiles") + local crate = {} crate.name = "FactoryHelper/ThrowBox" +crate.fieldInformation = function() + return { + impactParticlesColor = { + fieldType = "color" + }, + debrisFromTiletype = { + options = fakeTilesHelper.getTilesOptions(), + editable = false + } + } +end + crate.placements = { { name = "wood", @@ -9,7 +24,8 @@ crate.placements = { isMetal = false, tutorial = false, isSpecial = false, - isCrucial = false + isCrucial = false, + canPassThroughSpinners = false } }, { @@ -18,15 +34,74 @@ crate.placements = { isMetal = true, tutorial = false, isSpecial = false, - isCrucial = false + isCrucial = false, + canPassThroughSpinners = false + } + }, + { + name = "reskin_wood", + data = { + isMetal = false, + tutorial = false, + isSpecial = false, + isCrucial = false, + canPassThroughSpinners = false, + overrideTextures = true, + crateTexturePath = "objects/FactoryHelper/crate/crate0", + crucialTexturePath = "objects/FactoryHelper/crate/crucial", + overrideParticles = false, + impactParticlesColor = "9c8d7b", + overrideDebris = false, + debrisFromTiletype = '9' + } + }, + { + name = "reskin_metal", + data = { + isMetal = true, + tutorial = false, + isSpecial = false, + isCrucial = false, + canPassThroughSpinners = false, + overrideTextures = true, + crateTexturePath = "objects/FactoryHelper/crate/crate_metal0", + crucialTexturePath = "objects/FactoryHelper/crate/crucial", + overrideParticles = false, + impactParticlesColor = "9c8d7b", + overrideDebris = false, + debrisFromTiletype = '8' } } } -function crate.texture(sprite, entity) - return entity.isMetal and "objects/FactoryHelper/crate/crate_metal0" or "objects/FactoryHelper/crate/crate0" -end +local justification = {0.0, 0.0} + +function crate.sprite(room, entity) + local sprites = {} + + if (entity.overrideTextures) then + local crateSprite = drawableSprite.fromTexture(entity.crateTexturePath or "", entity) + crateSprite:setJustification(justification) + table.insert(sprites, crateSprite) -crate.justification = {0.0, 0.0} + if (entity.isCrucial) then + local crucialSprite = drawableSprite.fromTexture(entity.crucialTexturePath or "", entity) + crucialSprite:setJustification(justification) + table.insert(sprites, crucialSprite) + end + else + local crateSprite = drawableSprite.fromTexture(entity.isMetal and "objects/FactoryHelper/crate/crate_metal0" or "objects/FactoryHelper/crate/crate0", entity) + crateSprite:setJustification(justification) + table.insert(sprites, crateSprite) + + if (entity.isCrucial) then + local crucialSprite = drawableSprite.fromTexture("objects/FactoryHelper/crate/crucial", entity) + crucialSprite:setJustification(justification) + table.insert(sprites, crucialSprite) + end + end + + return sprites +end return crate diff --git a/Loenn/entities/crate_spawner.lua b/Loenn/entities/crate_spawner.lua index 51bb22b..07a74e7 100644 --- a/Loenn/entities/crate_spawner.lua +++ b/Loenn/entities/crate_spawner.lua @@ -1,19 +1,33 @@ local drawableRectangle = require("structs.drawable_rectangle") local drawableSprite = require("structs.drawable_sprite") +local fakeTilesHelper = require("helpers.fake_tiles") local crateSpawner = {} crateSpawner.name = "FactoryHelper/ThrowBoxSpawner" -crateSpawner.fieldInformation = { - delay = { - minimumValue = 0.0 - }, - maximum = { - fieldType = "integer", - minimumValue = 0 +crateSpawner.fieldInformation = function() + return { + delay = { + minimumValue = 0.0 + }, + maximum = { + fieldType = "integer", + minimumValue = 0 + }, + impactParticlesColor = { + fieldType = "color" + }, + debrisFromTiletype = { + options = fakeTilesHelper.getTilesOptions(), + editable = false + }, + metalDebrisFromTiletype = { + options = fakeTilesHelper.getTilesOptions(), + editable = false + } } -} +end crateSpawner.placements = { { @@ -26,7 +40,30 @@ crateSpawner.placements = { isRandom = false, fromTop = true, tutorial = false, - startActive = true + startActive = true, + canPassThroughSpinners = false + } + }, + { + name = "crate_spawner_reskin", + data = { + delay = 1.0, + maximum = 0, + activationId = "", + isMetal = false, + isRandom = false, + fromTop = true, + tutorial = false, + startActive = true, + canPassThroughSpinners = false, + overrideTextures = true, + woodenCrateTexturePath = "objects/FactoryHelper/crate/crate0", + metalCrateTexturePath = "objects/FactoryHelper/crate/crate_metal0", + overrideParticles = false, + impactParticlesColor = "9c8d7b", + overrideDebris = false, + woodenDebrisFromTiletype = '9', + metalDebrisFromTiletype = '8' } } } @@ -37,7 +74,13 @@ local randomColor = {1.0, 0.5, 1.0, 0.2} function crateSpawner.sprite(room, entity) local sprites = {} - local texture = entity.isMetal and "objects/FactoryHelper/crate/crate_metal0" or "objects/FactoryHelper/crate/crate0" + local texture = "" + if (entity.overrideTextures) then + texture = entity.isMetal and (entity.metalCrateTexturePath or "") or (entity.woodenCrateTexturePath or "") + else + texture = entity.isMetal and "objects/FactoryHelper/crate/crate_metal0" or "objects/FactoryHelper/crate/crate0" + end + local crateSprite = drawableSprite.fromTexture(texture, entity) crateSprite.color = {1.0, 1.0, 1.0, 0.5} diff --git a/Loenn/entities/wind_tunnel.lua b/Loenn/entities/wind_tunnel.lua index 7beec08..c2b65fd 100644 --- a/Loenn/entities/wind_tunnel.lua +++ b/Loenn/entities/wind_tunnel.lua @@ -8,6 +8,13 @@ windTunnel.fieldInformation = { direction = { editable = false, options = directions + }, + particleColors = { + fieldType = "list", + elementOptions = { + fieldType = "color" + }, + elementDefault = "ffffff" } } @@ -22,7 +29,7 @@ for _, direction in ipairs(directions) do activationId = "", strength = 100.0, startActive = true, - particleColors = "808080,545151,ada5a5", + particleColors = "808080,545151,ada5a5", showParticles = true } } diff --git a/Loenn/lang/en_gb.lang b/Loenn/lang/en_gb.lang index 4b94f82..dca5d0e 100644 --- a/Loenn/lang/en_gb.lang +++ b/Loenn/lang/en_gb.lang @@ -29,13 +29,24 @@ entities.FactoryHelper/Conveyor.attributes.description.startLeft=If `true` the c # Crate entities.FactoryHelper/ThrowBox.placements.name.wood=Crate (Wood) entities.FactoryHelper/ThrowBox.placements.name.metal=Crate (Metal) +entities.FactoryHelper/ThrowBox.placements.name.reskin_wood=Crate (Reskinnable, Wood) +entities.FactoryHelper/ThrowBox.placements.name.reskin_metal=Crate (Reskinnable, Metal) entities.FactoryHelper/ThrowBox.attributes.description.isMetal=Whether the crate is metallic or not. If `false` it will be wooden.\nWooden crates will break on high-speed impacts. entities.FactoryHelper/ThrowBox.attributes.description.tutorial=Whether the tutorial messsages should show up. entities.FactoryHelper/ThrowBox.attributes.description.isSpecial=Whether the crate should kill the player and return them to the crate's origin upon destruction or player death. entities.FactoryHelper/ThrowBox.attributes.description.isCrucial=Whether the crate should kill the player upon destruction. +entities.FactoryHelper/ThrowBox.attributes.description.canPassThroughSpinners=Whether the crate should not break in collision with Spinners. +entities.FactoryHelper/ThrowBox.attributes.description.overrideTextures=Whether to use custom texture paths instead of the default ones, regardless of the type of the crate. +entities.FactoryHelper/ThrowBox.attributes.description.crateTexturePath=What texture to use for the crate.\nDefault value is "objects/FactoryHelper/crate/crate0" if wooden, or "objects/FactoryHelper/crate/crate_metal0" if metal.\nChanging this has no effect if `Override Textures` is `false`. +entities.FactoryHelper/ThrowBox.attributes.description.crucialTexturePath=What texture to add to the crate if `Is Crucial` is `true`.\nDefault value is "objects/FactoryHelper/crate/crucial".\nChanging this has no effect if `Override Textures` is `false`. +entities.FactoryHelper/ThrowBox.attributes.description.overrideParticles=Whether to create a new impact particle type for the crate, to be used instead of the default. +entities.FactoryHelper/ThrowBox.attributes.description.impactParticlesColor=The color in hexadecimal for the particles emitted when the crate hits a solid. Default value is "9c8d7b".\nChanging this has no effect if `Override Particles` is `false`. +entities.FactoryHelper/ThrowBox.attributes.description.overrideDebris=Whether to use a different type of debris that will appear when the crate breaks regardless of its type. +entities.FactoryHelper/ThrowBox.attributes.description.debrisFromTiletype=From which tiletype to use the debris that will appear when the crate breaks.\nDefault value is '9' (Wood) if wooden, or '8' (Rock) if metal.\nChanging this has no effect if `Override Debris` is `false`.\nYou can create new debris types by adding the `debris` attribute to modded tilesets in your `ForegroundTiles.xml`.\nSee https://github.com/EverestAPI/Resources/wiki/Custom-Tilesets#adding-custom-debris for details. # Crate Spawner entities.FactoryHelper/ThrowBoxSpawner.placements.name.crate_spawner=Crate Spawner +entities.FactoryHelper/ThrowBoxSpawner.placements.name.crate_spawner_reskin=Crate Spawner (Reskinnable) entities.FactoryHelper/ThrowBoxSpawner.attributes.description.activationId=String value of the entity's activator ID. When a factory activator with this ID is turned on, the entity will toggle state. entities.FactoryHelper/ThrowBoxSpawner.attributes.description.startActive=If `true` the entity will initially be active. entities.FactoryHelper/ThrowBoxSpawner.attributes.description.delay=The amount of time before a new crate is spawned after the previous one in seconds. @@ -44,6 +55,15 @@ entities.FactoryHelper/ThrowBoxSpawner.attributes.description.isRandom=Whether t entities.FactoryHelper/ThrowBoxSpawner.attributes.description.fromTop=If `true` they will spawn on top, otherwise they will spawn at the nearest side. Defaults to `true`. entities.FactoryHelper/ThrowBoxSpawner.attributes.description.tutorial=Whether the tutorial messsages should show up. entities.FactoryHelper/ThrowBoxSpawner.attributes.description.maximum=The maximum number of crate this spawner can spawn. Destroyed crates do not count towards the maximum. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.canPassThroughSpinners=Whether the spawned crates should not break in collision with Spinners. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.overrideTextures=Whether to use custom texture paths instead of the default ones for spawned crates depending on their type. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.woodenCrateTexturePath=What texture to use for spawned crates if they are wooden.\nDefault value is "objects/FactoryHelper/crate/crate0".\nChanging this has no effect if `Override Textures` is `false`. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.metalCrateTexturePath=What texture to use for spawned crates if they are metal.\nDefault value is "objects/FactoryHelper/crate/crate_metal0".\nChanging this has no effect if `Override Textures` is `false`. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.overrideParticles=Whether to create a new impact particle type for spawned crates, to be used instead of the default. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.impactParticlesColor=The color in hexadecimal for the particles emitted when spawned crates hit a solid. Default value is "9c8d7b".\nChanging this has no effect if `Override Particles` is `false`. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.overrideDebris=Whether to use a different type of debris that will appear when spawned crates break depending on their type. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.woodenDebrisFromTiletype=From which tiletype to use the debris that will appear when spawned crates break if they are wooden.\nDefault value is '9' (Wood).\nChanging this has no effect if `Override Debris` is `false`.\nYou can create new debris types by adding the `debris` attribute to modded tilesets in your `ForegroundTiles.xml`.\nSee https://github.com/EverestAPI/Resources/wiki/Custom-Tilesets#adding-custom-debris for details. +entities.FactoryHelper/ThrowBoxSpawner.attributes.description.metalDebrisFromTiletype=From which tiletype to use the debris that will appear when spawned crates break if they are metal.\nDefault value is '8' (Rock).\nChanging this has no effect if `Override Debris` is `false`.\nYou can create new debris types by adding the `debris` attribute to modded tilesets in your `ForegroundTiles.xml`.\nSee https://github.com/EverestAPI/Resources/wiki/Custom-Tilesets#adding-custom-debris for details. # Dash Fuse Box entities.FactoryHelper/DashFuseBox.placements.name.left=Dash Fuse Box (Left)