From 0877aee3e6d7fe2c03324fdd2f7313dd0cb60f45 Mon Sep 17 00:00:00 2001 From: Kam Date: Wed, 19 Mar 2025 03:32:07 -0400 Subject: [PATCH] Custom Particle Trails for NPCs + Ranged Amplification --- src/main/java/noppes/npcs/DataStats.java | 8 +- .../npcs/client/gui/SubGuiNpcProjectiles.java | 96 +++++++++++----- .../npcs/client/gui/SubGuiScriptParticle.java | 107 ++++++++++++++++++ .../noppes/npcs/entity/EntityProjectile.java | 28 ++++- .../assets/customnpcs/lang/en_US.lang | 23 ++++ 5 files changed, 227 insertions(+), 35 deletions(-) create mode 100644 src/main/java/noppes/npcs/client/gui/SubGuiScriptParticle.java diff --git a/src/main/java/noppes/npcs/DataStats.java b/src/main/java/noppes/npcs/DataStats.java index d1bbd06ad..f0fb3ae4c 100644 --- a/src/main/java/noppes/npcs/DataStats.java +++ b/src/main/java/noppes/npcs/DataStats.java @@ -7,6 +7,7 @@ import noppes.npcs.constants.EnumParticleType; import noppes.npcs.constants.EnumPotionType; import noppes.npcs.controllers.data.MagicData; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.scripted.ScriptParticle; import noppes.npcs.util.ValueUtil; public class DataStats { @@ -45,6 +46,7 @@ public class DataStats { public boolean pRender3D = false, pSpin = false, pStick = false; public EnumPotionType pEffect = EnumPotionType.None; public EnumParticleType pTrail = EnumParticleType.None; + public ScriptParticle pCustom = new ScriptParticle(""); public int pEffAmp = 0; public String fireSound = "random.bow"; public boolean aimWhileShooting = false; @@ -110,6 +112,8 @@ public class DataStats { compound.setString("FiringSound", fireSound); compound.setBoolean("AimWhileShooting", aimWhileShooting); compound.setBoolean("ProjectilesKeepTerrain", projectilesKeepTerrain); + if(pTrail == EnumParticleType.Custom) + compound.setTag("pCustom", pCustom.writeToNBT()); compound.setBoolean("ImmuneToFire", immuneToFire); compound.setBoolean("PotionImmune", potionImmune); @@ -174,7 +178,9 @@ public class DataStats { fireSound = compound.getString("FiringSound"); aimWhileShooting = compound.getBoolean("AimWhileShooting"); projectilesKeepTerrain = compound.getBoolean("ProjectilesKeepTerrain"); - + if(pTrail == EnumParticleType.Custom){ + pCustom = ScriptParticle.fromNBT(compound.getCompoundTag("pCustom")); + } immuneToFire = compound.getBoolean("ImmuneToFire"); potionImmune = compound.getBoolean("PotionImmune"); drowningType = compound.getInteger("DrowningType"); diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiNpcProjectiles.java b/src/main/java/noppes/npcs/client/gui/SubGuiNpcProjectiles.java index 2d86e7370..40b8afe72 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiNpcProjectiles.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiNpcProjectiles.java @@ -6,7 +6,7 @@ import noppes.npcs.client.gui.util.*; import noppes.npcs.constants.EnumParticleType; import noppes.npcs.constants.EnumPotionType; -public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldListener { +public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldListener, ISubGuiListener { private DataStats stats; private String[] potionNames = new String[]{"gui.none", "tile.fire.name", "potion.poison", "potion.hunger", "potion.weakness", "potion.moveSlowdown", "potion.confusion", "potion.blindness", "potion.wither"}; private String[] trailNames = new String[]{ "gui.none", "trail.smoke", "trail.portal", "trail.redstone", @@ -18,7 +18,7 @@ public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldL public SubGuiNpcProjectiles(DataStats stats) { this.stats = stats; setBackground("menubg.png"); - xSize = 300; + xSize = 390; ySize = 216; closeOnEsc = true; } @@ -26,55 +26,82 @@ public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldL public void initGui() { super.initGui(); addLabel(new GuiNpcLabel(1, "enchantment.arrowDamage", guiLeft + 5, guiTop + 15)); - addTextField(new GuiNpcTextField(1, this, fontRendererObj, guiLeft + 45, guiTop + 10, 50, 18, String.format("%.0f", stats.pDamage) + "")); + addTextField(new GuiNpcTextField(1, this, fontRendererObj, guiLeft + 45, guiTop + 10, 50, 20, String.format("%.0f", stats.pDamage) + "")); getTextField(1).setFloatsOnly(); getTextField(1).setMinMaxDefaultFloat(0, Float.MAX_VALUE, 5); - addLabel(new GuiNpcLabel(2, "enchantment.arrowKnockback", guiLeft + 110, guiTop + 15)); - addTextField(new GuiNpcTextField(2, this, fontRendererObj, guiLeft + 150, guiTop + 10, 50, 18, stats.pImpact + "")); + + int y = guiTop + 10; + int second = guiLeft + 110; + + addLabel(new GuiNpcLabel(2, "enchantment.arrowKnockback", second, y + 5)); + addTextField(new GuiNpcTextField(2, this, fontRendererObj, second + 40, y, 50, 20, stats.pImpact + "")); getTextField(2).integersOnly = true; getTextField(2).setMinMaxDefault(0, Integer.MAX_VALUE, 0); - addLabel(new GuiNpcLabel(3, "stats.size", guiLeft + 5, guiTop + 45)); - addTextField(new GuiNpcTextField(3, this, fontRendererObj, guiLeft + 45, guiTop + 40, 50, 18, stats.pSize + "")); + + addButton(new GuiNpcButton(6, guiLeft + 220, guiTop + 10, 60, 20, new String[]{"stats.noglow", "stats.glows"}, stats.pGlows ? 1 : 0)); + + y += 30; + + addLabel(new GuiNpcLabel(3, "stats.size", guiLeft + 5, y + 5)); + addTextField(new GuiNpcTextField(3, this, fontRendererObj, guiLeft + 45, y, 50, 20, stats.pSize + "")); getTextField(3).integersOnly = true; getTextField(3).setMinMaxDefault(1, Integer.MAX_VALUE, 10); - addLabel(new GuiNpcLabel(4, "stats.speed", guiLeft + 5, guiTop + 75)); - addTextField(new GuiNpcTextField(4, this, fontRendererObj, guiLeft + 45, guiTop + 70, 50, 18, stats.pSpeed + "")); + + addLabel(new GuiNpcLabel(4, "stats.speed", second, y + 5)); + addTextField(new GuiNpcTextField(4, this, fontRendererObj, second + 40, y, 50, 20, stats.pSpeed + "")); getTextField(4).integersOnly = true; getTextField(4).setMinMaxDefault(1, Integer.MAX_VALUE, 10); - addLabel(new GuiNpcLabel(5, "stats.hasgravity", guiLeft + 5, guiTop + 105)); - addButton(new GuiNpcButton(0, guiLeft + 60, guiTop + 100, 60, 20, new String[]{"gui.no", "gui.yes"}, stats.pPhysics ? 1 : 0)); + y += 30; + + addLabel(new GuiNpcLabel(5, "stats.hasgravity", guiLeft + 5, y + 5)); + addButton(new GuiNpcButton(0, guiLeft + 80, y, 40, 20, new String[]{"gui.no", "gui.yes"}, stats.pPhysics ? 1 : 0)); if (!stats.pPhysics) { - addButton(new GuiNpcButton(1, guiLeft + 140, guiTop + 100, 60, 20, new String[]{"gui.constant", "gui.accelerate"}, stats.pXlr8 ? 1 : 0)); + addButton(new GuiNpcButton(1, guiLeft + 125, y, 60, 20, new String[]{"gui.constant", "gui.accelerate"}, stats.pXlr8 ? 1 : 0)); } - addLabel(new GuiNpcLabel(6, "stats.explosive", guiLeft + 5, guiTop + 135)); - addButton(new GuiNpcButton(2, guiLeft + 60, guiTop + 130, 60, 20, new String[]{"gui.no", "gui.yes"}, stats.pExplode ? 1 : 0)); + + y += 30; + + addLabel(new GuiNpcLabel(6, "stats.explosive", guiLeft + 5, y + 5)); + addButton(new GuiNpcButton(2, guiLeft + 80, y, 40, 20, new String[]{"gui.no", "gui.yes"}, stats.pExplode ? 1 : 0)); if (stats.pExplode) { - addButton(new GuiNpcButton(3, guiLeft + 140, guiTop + 130, 60, 20, new String[]{"gui.none", "gui.small", "gui.medium", "gui.large"}, stats.pArea)); + addButton(new GuiNpcButton(3, guiLeft + 125, y, 60, 20, new String[]{"gui.none", "gui.small", "gui.medium", "gui.large"}, stats.pArea)); } - addLabel(new GuiNpcLabel(7, "stats.rangedeffect", guiLeft + 5, guiTop + 165)); - addButton(new GuiNpcButton(4, guiLeft + 60, guiTop + 160, 60, 20, potionNames, stats.pEffect.ordinal())); + + addLabel(new GuiNpcLabel(7, "stats.rangedeffect", guiLeft + 210, y + 5)); + addButton(new GuiButtonBiDirectional(4, guiLeft + 280, y, 100, 20, potionNames, stats.pEffect.ordinal())); + if (stats.pEffect != EnumPotionType.None) { - addTextField(new GuiNpcTextField(5, this, fontRendererObj, guiLeft + 140, guiTop + 160, 60, 18, stats.pDur + "")); + int internalY = y + 30; + addLabel(new GuiNpcLabel(50, "gui.time", guiLeft + 210, internalY + 5)); + addTextField(new GuiNpcTextField(5, this, fontRendererObj, guiLeft + 280, internalY, 40, 20, stats.potionDuration + "")); getTextField(5).integersOnly = true; getTextField(5).setMinMaxDefault(1, Integer.MAX_VALUE, 5); - if (stats.pEffect != EnumPotionType.Fire) { - addButton(new GuiNpcButton(10, guiLeft + 210, guiTop + 160, 40, 20, new String[]{"stats.regular", "stats.amplified"}, stats.pEffAmp)); + if (stats.potionType != EnumPotionType.Fire) { + internalY += 30; + addLabel(new GuiNpcLabel(70, "stats.amplify", guiLeft + 210, internalY + 5)); + addButton(new GuiButtonBiDirectional(10, guiLeft + 280, internalY, 52, 20, new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}, stats.pEffAmp)); } } - addLabel(new GuiNpcLabel(8, "stats.trail", guiLeft + 5, guiTop + 195)); - addButton(new GuiNpcButton(5, guiLeft + 60, guiTop + 190, 60, 20, trailNames, stats.pTrail.ordinal())); + y += 30; - addButton(new GuiNpcButton(7, guiLeft + 220, guiTop + 10, 30, 20, new String[]{"2D", "3D"}, stats.pRender3D ? 1 : 0)); - if (stats.pRender3D) { - addLabel(new GuiNpcLabel(10, "stats.spin", guiLeft + 160, guiTop + 45)); - addButton(new GuiNpcButton(8, guiLeft + 220, guiTop + 40, 30, 20, new String[]{"gui.no", "gui.yes"}, stats.pSpin ? 1 : 0)); - addLabel(new GuiNpcLabel(11, "stats.stick", guiLeft + 160, guiTop + 75)); - addButton(new GuiNpcButton(9, guiLeft + 220, guiTop + 70, 30, 20, new String[]{"gui.no", "gui.yes"}, stats.pStick ? 1 : 0)); + addLabel(new GuiNpcLabel(8, "stats.trail", guiLeft + 5, y + 5)); + addButton(new GuiNpcButton(5, guiLeft + 80, y, 75, 20, trailNames, stats.pTrail.ordinal())); + if(stats.pTrail == EnumParticleType.Custom){ + int internalY = y + 30; + addLabel(new GuiNpcLabel(90, "trail.custom", guiLeft + 5, internalY + 5)); + addButton(new GuiNpcButton(90, guiLeft + 80, internalY, 75, 20, "gui.edit")); } - addButton(new GuiNpcButton(6, guiLeft + 140, guiTop + 190, 60, 20, new String[]{"stats.noglow", "stats.glows"}, stats.pGlows ? 1 : 0)); - addButton(new GuiNpcButton(66, guiLeft + 210, guiTop + 190, 40, 20, "gui.done")); + + addButton(new GuiNpcButton(7, guiLeft + xSize - 40, guiTop + 10, 30, 20, new String[]{"2D", "3D"}, stats.pRender3D ? 1 : 0)); + if (stats.pRender3D) { + addLabel(new GuiNpcLabel(10, "stats.spin", guiLeft + xSize - 80, guiTop + 45)); + addButton(new GuiNpcButton(8, guiLeft + xSize - 40, guiTop + 40, 30, 20, new String[]{"gui.no", "gui.yes"}, stats.pSpin ? 1 : 0)); + addLabel(new GuiNpcLabel(11, "stats.stick", guiLeft + xSize - 80, guiTop + 75)); + addButton(new GuiNpcButton(9, guiLeft + xSize - 40, guiTop + 70, 30, 20, new String[]{"gui.no", "gui.yes"}, stats.pStick ? 1 : 0)); + } + addButton(new GuiNpcButton(66, guiLeft + xSize - 50, guiTop + 190, 40, 20, "gui.done")); } public void unFocused(GuiNpcTextField textfield) { @@ -113,6 +140,10 @@ public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldL } if (button.id == 5) { stats.pTrail = EnumParticleType.values()[button.getValue()]; + initGui(); + } + if (button.id == 90) { + setSubGui(new SubGuiScriptParticle(stats.pCustom)); } if (button.id == 6) { stats.pGlows = (button.getValue() == 1); @@ -134,4 +165,9 @@ public class SubGuiNpcProjectiles extends SubGuiInterface implements ITextfieldL close(); } } + + @Override + public void subGuiClosed(SubGuiInterface subgui) { + + } } diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiScriptParticle.java b/src/main/java/noppes/npcs/client/gui/SubGuiScriptParticle.java new file mode 100644 index 000000000..056335dfe --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/SubGuiScriptParticle.java @@ -0,0 +1,107 @@ +package noppes.npcs.client.gui; + +import net.minecraft.client.gui.GuiButton; +import noppes.npcs.client.gui.util.*; +import noppes.npcs.scripted.ScriptParticle; + +public class SubGuiScriptParticle extends SubGuiInterface implements ITextfieldListener { + + private ScriptParticle particle; + + public SubGuiScriptParticle(ScriptParticle particle) { + this.particle = particle; + setBackground("menubg.png"); + xSize = 350; + ySize = 216; + closeOnEsc = true; + } + + @Override + public void initGui() { + super.initGui(); + int y = guiTop + 10; + // Directory (long textfield) + addLabel(new GuiNpcLabel(1, "display.texture", guiLeft + 5, y)); + addTextField(new GuiNpcTextField(1, this, fontRendererObj, guiLeft + 70, y - 5, 260, 20, particle.directory)); + + y += 30; + // maxAge (small int textfield) + addLabel(new GuiNpcLabel(2, "particle.maxAge", guiLeft + 5, y)); + addTextField(new GuiNpcTextField(2, this, fontRendererObj, guiLeft + 70, y - 5, 50, 20, particle.maxAge + "")); + getTextField(2).integersOnly = true; + getTextField(2).setMinMaxDefault(1, Integer.MAX_VALUE, particle.maxAge); + + y += 30; + // Width and Height (small int textfields) + addLabel(new GuiNpcLabel(3, "effect.editor.width", guiLeft + 5, y)); + addTextField(new GuiNpcTextField(3, this, fontRendererObj, guiLeft + 70, y - 5, 50, 20, particle.width + "")); + getTextField(3).integersOnly = true; + getTextField(3).setMinMaxDefault(-1, Integer.MAX_VALUE, particle.width); + + addLabel(new GuiNpcLabel(4, "effect.editor.height", guiLeft + 140, y)); + addTextField(new GuiNpcTextField(4, this, fontRendererObj, guiLeft + 190, y - 5, 50, 20, particle.height + "")); + getTextField(4).integersOnly = true; + getTextField(4).setMinMaxDefault(-1, Integer.MAX_VALUE, particle.height); + + y += 30; + // FacePlayer, Glows, noClip buttons (Yes/No) with width 40 + addLabel(new GuiNpcLabel(5, "particle.facePlayer", guiLeft + 5, y + 5)); + addButton(new GuiNpcButton(5, guiLeft + 70, y, 40, 20, new String[]{"gui.no", "gui.yes"}, particle.facePlayer ? 1 : 0)); + + addLabel(new GuiNpcLabel(6, "display.glow", guiLeft + 120, y + 5)); + addButton(new GuiNpcButton(6, guiLeft + 170, y, 40, 20, new String[]{"gui.no", "gui.yes"}, particle.glows ? 1 : 0)); + + addLabel(new GuiNpcLabel(7, "particle.noClip", guiLeft + 220, y + 5)); + addButton(new GuiNpcButton(7, guiLeft + 280, y, 40, 20, new String[]{"gui.no", "gui.yes"}, particle.noClip ? 1 : 0)); + + y += 30; + // Scale row: label "model.scale", then "X:" and "Y:" textfields. + addLabel(new GuiNpcLabel(8, "model.scale", guiLeft + 5, y + 5)); + addLabel(new GuiNpcLabel(9, "X:", guiLeft + 100, y + 5)); + addTextField(new GuiNpcTextField(8, this, fontRendererObj, guiLeft + 120, y - 5, 40, 20, particle.scaleX1 + "")); + addLabel(new GuiNpcLabel(10, "Y:", guiLeft + 170, y + 5)); + addTextField(new GuiNpcTextField(9, this, fontRendererObj, guiLeft + 190, y - 5, 40, 20, particle.scaleY1 + "")); + + // Done button at bottom right + addButton(new GuiNpcButton(66, guiLeft + xSize - 50, guiTop + ySize - 30, 45, 20, "gui.done")); + } + + @Override + public void unFocused(GuiNpcTextField textfield) { + if (textfield.id == 1) { + particle.directory = textfield.getText(); + } else if (textfield.id == 2) { + particle.maxAge = textfield.getInteger(); + } else if (textfield.id == 3) { + particle.width = textfield.getInteger(); + } else if (textfield.id == 4) { + particle.height = textfield.getInteger(); + } else if (textfield.id == 8) { + // When scaleX1 is modified, update both scaleX1 and scaleX2. + float value = Float.parseFloat(textfield.getText()); + particle.scaleX1 = value; + particle.scaleX2 = value; + } else if (textfield.id == 9) { + // Update scaleY1 and scaleY2. + float value = Float.parseFloat(textfield.getText()); + particle.scaleY1 = value; + particle.scaleY2 = value; + } + } + + @Override + protected void actionPerformed(GuiButton guibutton) { + if (guibutton.id == 5) { + particle.facePlayer = (((GuiNpcButton) guibutton).getValue() == 1); + } + if (guibutton.id == 6) { + particle.glows = (((GuiNpcButton) guibutton).getValue() == 1); + } + if (guibutton.id == 7) { + particle.noClip = (((GuiNpcButton) guibutton).getValue() == 1); + } + if (guibutton.id == 66) { + close(); + } + } +} diff --git a/src/main/java/noppes/npcs/entity/EntityProjectile.java b/src/main/java/noppes/npcs/entity/EntityProjectile.java index b7d9e0a48..29449e404 100644 --- a/src/main/java/noppes/npcs/entity/EntityProjectile.java +++ b/src/main/java/noppes/npcs/entity/EntityProjectile.java @@ -3,7 +3,11 @@ package noppes.npcs.entity; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import kamkeel.npcs.addon.DBCAddon; +import kamkeel.npcs.network.PacketClient; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.packets.data.script.ScriptedParticlePacket; import net.minecraft.block.Block; +import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityItem; @@ -31,10 +35,12 @@ import noppes.npcs.api.IPos; import noppes.npcs.api.IWorld; import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.entity.IProjectile; +import noppes.npcs.client.fx.CustomFX; import noppes.npcs.constants.EnumParticleType; import noppes.npcs.constants.EnumPotionType; import noppes.npcs.controllers.ScriptContainer; import noppes.npcs.scripted.NpcAPI; +import noppes.npcs.scripted.ScriptParticle; import noppes.npcs.scripted.event.ProjectileEvent; import noppes.npcs.util.IProjectileCallback; @@ -381,12 +387,16 @@ public class EntityProjectile extends EntityThrowable { this.motionZ += this.accelerationZ; } - if (worldObj.isRemote && !this.dataWatcher.getWatchableObjectString(22).equals("")) { + if (worldObj.isRemote && !this.dataWatcher.getWatchableObjectString(22).isEmpty()) { String particle = this.dataWatcher.getWatchableObjectString(22); - if(particle.equals("custom")){ - - } else { + if(!particle.equals("custom")){ this.worldObj.spawnParticle(particle, this.posX, this.posY, this.posZ, 0.0D, 0.0D, 0.0D); + + } + } else if (!worldObj.isRemote){ + if(this.npc != null && this.npc.stats.pTrail == EnumParticleType.Custom){ + this.npc.stats.pCustom.setPosition(this.posX, this.posY, this.posZ); + spawnScriptedParticle(this.npc.stats.pCustom.writeToNBT(), this.npc); } } this.setPosition(this.posX, this.posY, this.posZ); @@ -394,6 +404,10 @@ public class EntityProjectile extends EntityThrowable { } } + public static void spawnScriptedParticle(NBTTagCompound compound, EntityNPCInterface entity) { + PacketHandler.Instance.sendTracking(new ScriptedParticlePacket(compound), entity); + } + public boolean isBlock() { ItemStack item = this.getItemDisplay(); if (item == null) @@ -668,6 +682,7 @@ public class EntityProjectile extends EntityThrowable { par1NBTTagCompound.setByte("Render3D", this.dataWatcher.getWatchableObjectByte(28)); par1NBTTagCompound.setByte("Spins", this.dataWatcher.getWatchableObjectByte(29)); par1NBTTagCompound.setByte("Sticks", this.dataWatcher.getWatchableObjectByte(30)); + par1NBTTagCompound.setByte("trailCustom", this.dataWatcher.getWatchableObjectByte(31)); } /** @@ -701,6 +716,7 @@ public class EntityProjectile extends EntityThrowable { this.dataWatcher.updateObject(28, Byte.valueOf((byte) (par1NBTTagCompound.getBoolean("Render3D") ? 1 : 0))); this.dataWatcher.updateObject(29, Byte.valueOf((byte) (par1NBTTagCompound.getBoolean("Spins") ? 1 : 0))); this.dataWatcher.updateObject(30, Byte.valueOf((byte) (par1NBTTagCompound.getBoolean("Sticks") ? 1 : 0))); + this.dataWatcher.updateObject(31, par1NBTTagCompound.getString("trailCustom")); if (this.throwerName != null && this.throwerName.length() == 0) { this.throwerName = null; @@ -801,6 +817,10 @@ public class EntityProjectile extends EntityThrowable { this.dataWatcher.updateObject(22, type.particleName); } + public void setCustomParticle(ScriptParticle particle) { + + } + public void setHasGravity(boolean bo) { this.dataWatcher.updateObject(26, Byte.valueOf((byte) (bo ? 1 : 0))); } diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 9ca3a624b..aeb6cf125 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1330,6 +1330,10 @@ effect.editor.xPos=X Pos effect.editor.yPos=Y Pos effect.editor.lossOnDeath=Loss on Death +particle.maxAge=Max Age +particle.facePlayer=Face Player +particle.noClip=No Clip + profile.lastLoaded=Last Loaded profile.info.quest.active=Quests Active profile.info.quest.finished=Quests Finished @@ -1371,3 +1375,22 @@ rpgcore:attribute.resistance=Resistance rpgcore:attribute.neutral=Neutral Damage requirement.soulbind=Soulbind + +trail.smoke=Smoke +trail.portal=Portal +trail.redstone=Redstone +trail.lightning=Lightning +trail.largesmoke=Large Smoke +trail.magic=Magic +trail.enchant=Enchant +trail.crit=Critical +trail.explode=Explode +trail.music=Music +trail.flame=Flame +trail.lava=Lava +trail.splash=Splash +trail.slime=Slime +trail.heart=Heart +trail.angryvillager=AngryVillager +trail.happyvillager=HappyVillager +trail.custom=Custom