diff --git a/src/main/java/noppes/npcs/CommonProxy.java b/src/main/java/noppes/npcs/CommonProxy.java index 067e2f1b3..d814ccf86 100644 --- a/src/main/java/noppes/npcs/CommonProxy.java +++ b/src/main/java/noppes/npcs/CommonProxy.java @@ -1,7 +1,11 @@ package noppes.npcs; import cpw.mods.fml.common.network.IGuiHandler; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.client.Minecraft; import net.minecraft.client.model.ModelBiped; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; @@ -14,19 +18,108 @@ import noppes.npcs.api.IWorld; import noppes.npcs.blocks.tiles.TileNpcContainer; import noppes.npcs.constants.EnumGuiType; import noppes.npcs.containers.*; +import noppes.npcs.controllers.data.AnimationData; +import noppes.npcs.controllers.data.Frame; import noppes.npcs.controllers.data.PlayerData; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.util.MillisTimer; + +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.HashSet; public class CommonProxy implements IGuiHandler { - - public boolean newVersionAvailable = false; - public int revision = 1; + public final static HashSet clientPlayingAnimations = new HashSet<>(); + public final static HashSet serverPlayingAnimations = new HashSet<>(); + protected MillisTimer animationTimer = new MillisTimer(1000); + protected static long totalServerTicks; + protected static long totalClientTicks; public void load() { + this.createAnimationThread(); + CustomNpcs.Channel.register(new PacketHandlerServer()); CustomNpcs.ChannelPlayer.register(new PacketHandlerPlayer()); } + protected void createAnimationThread() { + Thread thread = (new Thread("Animation Thread") { public void run() { + while (true) { + try { + if (!CustomNpcs.proxy.hasClient() || !Minecraft.getMinecraft().isGamePaused()) { + animationTimer.updateTimer(); + } + + for (int i = 0; i < animationTimer.elapsedTicks; ++i) { + updateData(totalClientTicks, clientPlayingAnimations); + totalClientTicks++; + + updateData(totalServerTicks, serverPlayingAnimations); + totalServerTicks++; + } + + totalClientTicks %= Long.MAX_VALUE; + totalServerTicks %= Long.MAX_VALUE; + + synchronized (clientPlayingAnimations) { + clientPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); + } + synchronized (serverPlayingAnimations) { + serverPlayingAnimations.removeIf(CommonProxy.this::removeAnimation); + } + } catch (Exception e) { + if (!(e instanceof ConcurrentModificationException)) { + e.printStackTrace(); + } + } + } + }}); + thread.setDaemon(true); + thread.start(); + } + + private void updateData(long ticks, Collection animationData) { + for (AnimationData data : animationData) { + Frame frame = data.animation != null && data.animation.currentFrame() != null + ? (Frame) data.animation.currentFrame() : null; + if (frame != null) { + int tickDuration = frame.tickDuration(); + if (ticks % tickDuration == 0) { + data.increaseTime(); + } + } + } + } + + private boolean removeAnimation(AnimationData data) { + Entity entity = null; + if (data.parent instanceof DataDisplay) { + entity = ((DataDisplay)data.parent).npc; + } else { + if (data.parent instanceof PlayerData) { + entity = ((PlayerData) data.parent).player; + } else if (data.parent instanceof EntityPlayer) { + entity = (EntityPlayer) data.parent; + } + } + + if (entity == null) return true; + + if (entity.worldObj != null && entity.worldObj.isRemote) { + return clientRemoveAnimation(entity); + } else { + return entity.worldObj == null || !entity.worldObj.loadedEntityList.contains(entity) + || CustomNpcs.getServer().isServerStopped(); + } + } + + @SideOnly(Side.CLIENT) + private boolean clientRemoveAnimation(Entity entity) { + Minecraft mc = Minecraft.getMinecraft(); + return mc.theWorld == null + || mc.currentScreen == null && entity != mc.thePlayer && !mc.theWorld.loadedEntityList.contains(entity); + } + public PlayerData getPlayerData(EntityPlayer player) { return null; } @@ -166,4 +259,4 @@ public class CommonProxy implements IGuiHandler { public boolean isGUIOpen(){ return false; } -} \ No newline at end of file +} diff --git a/src/main/java/noppes/npcs/EventHooks.java b/src/main/java/noppes/npcs/EventHooks.java index 43e6d570f..aa95c8af7 100644 --- a/src/main/java/noppes/npcs/EventHooks.java +++ b/src/main/java/noppes/npcs/EventHooks.java @@ -15,11 +15,12 @@ import net.minecraft.world.WorldServer; import net.minecraftforge.event.entity.EntityEvent; import net.minecraftforge.event.world.WorldEvent; import noppes.npcs.api.IWorld; -import noppes.npcs.api.entity.IEntity; -import noppes.npcs.api.entity.IPlayer; -import noppes.npcs.api.entity.IProjectile; +import noppes.npcs.api.entity.*; +import noppes.npcs.api.event.IAnimationEvent; import noppes.npcs.api.gui.ICustomGui; import noppes.npcs.api.gui.IItemSlot; +import noppes.npcs.api.handler.data.IAnimation; +import noppes.npcs.api.handler.data.IFrame; import noppes.npcs.api.item.IItemCustom; import noppes.npcs.api.item.IItemStack; import noppes.npcs.constants.EnumScriptType; @@ -119,7 +120,6 @@ public class EventHooks { return; noppes.npcs.scripted.event.NpcEvent.InitEvent event = new noppes.npcs.scripted.event.NpcEvent.InitEvent(npc.wrappedNPC); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.INIT, event); npc.script.callScript(EnumScriptType.INIT, event); NpcAPI.EVENT_BUS.post(event); npc.advanced.soulStoneInit = false; @@ -130,7 +130,6 @@ public class EventHooks { return; noppes.npcs.scripted.event.NpcEvent.UpdateEvent event = new noppes.npcs.scripted.event.NpcEvent.UpdateEvent(npc.wrappedNPC); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.TICK, event); npc.script.callScript(EnumScriptType.TICK, event); NpcAPI.EVENT_BUS.post(event); } @@ -140,7 +139,6 @@ public class EventHooks { return false; NpcEvent.DialogEvent event = new NpcEvent.DialogEvent(npc.wrappedNPC, player, dialogId, optionId, dialog); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.DIALOG, event); npc.script.callScript(EnumScriptType.DIALOG, event, "player", event.getPlayer(), "dialog", event.getDialogId(), "option", event.getOptionId(), "dialogObj", event.getDialog()); return NpcAPI.EVENT_BUS.post(event); @@ -151,7 +149,6 @@ public class EventHooks { return; NpcEvent.DialogClosedEvent event = new NpcEvent.DialogClosedEvent(npc.wrappedNPC, player, dialogId, optionId, dialog); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.DIALOG_CLOSE, event); npc.script.callScript(EnumScriptType.DIALOG_CLOSE, event, "player", event.getPlayer(), "dialog", event.getDialogId(), "option", event.getOptionId(), "dialogObj", event.getDialog()); NpcAPI.EVENT_BUS.post(event); @@ -162,7 +159,6 @@ public class EventHooks { return false; NpcEvent.InteractEvent event = new NpcEvent.InteractEvent(npc.wrappedNPC,player); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.INTERACT, event); boolean result = npc.script.callScript(EnumScriptType.INTERACT, event, "player", player); NpcAPI.EVENT_BUS.post(event); return result; @@ -172,7 +168,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ATTACK_MELEE, event); npc.script.callScript(EnumScriptType.ATTACK_MELEE, event, "target", event.target); npc.script.callScript(EnumScriptType.ATTACK, event, "target", event.target); return NpcAPI.EVENT_BUS.post(event); @@ -182,7 +177,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.ATTACK_SWING, event); npc.script.callScript(EnumScriptType.ATTACK_SWING, event); return NpcAPI.EVENT_BUS.post(event); } @@ -191,7 +185,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.RANGED_LAUNCHED, event); npc.script.callScript(EnumScriptType.RANGED_LAUNCHED, event, "target", event.target); npc.script.callScript(EnumScriptType.ATTACK, event, "target", event.target); return NpcAPI.EVENT_BUS.post(event); @@ -202,7 +195,6 @@ public class EventHooks { return; NpcEvent.KilledEntityEvent event = new NpcEvent.KilledEntityEvent(npc.wrappedNPC,entity); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.KILLS, event); npc.script.callScript(EnumScriptType.KILLS, event, "target", entity); NpcAPI.EVENT_BUS.post(event); } @@ -211,7 +203,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.TARGET, event); npc.script.callScript(EnumScriptType.TARGET, event); return NpcAPI.EVENT_BUS.post(event); } @@ -221,7 +212,6 @@ public class EventHooks { return false; NpcEvent.TargetLostEvent event = new NpcEvent.TargetLostEvent(npc.wrappedNPC, prevtarget, newTarget); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.TARGET_LOST, event); npc.script.callScript(EnumScriptType.TARGET_LOST, event); return NpcAPI.EVENT_BUS.post(event); } @@ -231,7 +221,6 @@ public class EventHooks { return; NpcEvent.CollideEvent event = new NpcEvent.CollideEvent(npc.wrappedNPC,entity); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.COLLIDE, event); npc.script.callScript(EnumScriptType.COLLIDE, event, "entity", entity); NpcAPI.EVENT_BUS.post(event); } @@ -240,7 +229,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.DAMAGED, event); npc.script.callScript(EnumScriptType.DAMAGED, event); return NpcAPI.EVENT_BUS.post(event); } @@ -249,7 +237,6 @@ public class EventHooks { if(npc == null || npc.wrappedNPC == null) return false; - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.KILLED, event); npc.script.callScript(EnumScriptType.KILLED, event); return NpcAPI.EVENT_BUS.post(event); } @@ -259,14 +246,12 @@ public class EventHooks { return; NpcEvent.TimerEvent event = new NpcEvent.TimerEvent(npc.wrappedNPC, id); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.TIMER, event); npc.script.callScript(EnumScriptType.TIMER, event); NpcAPI.EVENT_BUS.post(event); } public static void onProjectileTick(EntityProjectile projectile) { ProjectileEvent.UpdateEvent event = new ProjectileEvent.UpdateEvent((IProjectile) NpcAPI.Instance().getIEntity(projectile)); - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.PROJECTILE_TICK, event); for(ScriptContainer script : projectile.scripts) { if(script.isValid()) { script.run(EnumScriptType.PROJECTILE_TICK, event); @@ -284,7 +269,6 @@ public class EventHooks { } public static void onProjectileImpact(EntityProjectile projectile, ProjectileEvent.ImpactEvent event) { - ScriptController.Instance.globalNpcScripts.callScript(EnumScriptType.PROJECTILE_IMPACT, event); for(ScriptContainer script : projectile.scripts) { if(script.isValid()) { script.run(EnumScriptType.PROJECTILE_IMPACT, event); @@ -803,4 +787,49 @@ public class EventHooks { handler.callScript(EnumScriptType.PARTY_DISBAND, event); NpcAPI.EVENT_BUS.post(event); } + + private static boolean postAnimationEvent(IAnimationEvent event) { + IScriptHandler handler; + IAnimatable animatable = event.getAnimation().getParent().getEntity(); + if (animatable instanceof ICustomNpc) { + EntityNPCInterface npc = (EntityNPCInterface) ((ICustomNpc) animatable).getMCEntity(); + handler = npc.script; + } else { + handler = ScriptController.Instance.playerScripts; + } + + if (handler.isClient()) + return false; + + handler.callScript(event.getHookName(), (Event) event); + return NpcAPI.EVENT_BUS.post((Event) event); + } + + public static boolean onAnimationStarted(IAnimation animation) { + if (animation.getParent() == null || animation.getParent().getEntity() == null) { + return false; + } + return postAnimationEvent(new AnimationEvent.Started(animation)); + } + + public static void onAnimationEnded(IAnimation animation) { + if (animation.getParent() == null || animation.getParent().getEntity() == null) { + return; + } + postAnimationEvent(new AnimationEvent.Ended(animation)); + } + + public static void onAnimationFrameEntered(IAnimation animation, IFrame frame) { + if (frame == null || animation.getParent() == null || animation.getParent().getEntity() == null) { + return; + } + postAnimationEvent(new AnimationEvent.FrameEvent.Entered(animation, frame)); + } + + public static void onAnimationFrameExited(IAnimation animation, IFrame frame) { + if (frame == null || animation.getParent() == null || animation.getParent().getEntity() == null) { + return; + } + postAnimationEvent(new AnimationEvent.FrameEvent.Exited(animation, frame)); + } } diff --git a/src/main/java/noppes/npcs/ServerEventsHandler.java b/src/main/java/noppes/npcs/ServerEventsHandler.java index 45af0cf13..f482fa0cc 100644 --- a/src/main/java/noppes/npcs/ServerEventsHandler.java +++ b/src/main/java/noppes/npcs/ServerEventsHandler.java @@ -479,12 +479,24 @@ public class ServerEventsHandler { @SubscribeEvent public void playerTracking(PlayerEvent.StartTracking event){ - if(!(event.target instanceof EntityNPCInterface) || event.target.worldObj.isRemote) - return; + if (event.target.worldObj.isRemote) return; - MarkData data = MarkData.get((EntityNPCInterface) event.target); - if(data.marks.isEmpty()) - return; - Server.sendData((EntityPlayerMP)event.entityPlayer, EnumPacketClient.MARK_DATA, event.target.getEntityId(), data.getNBT()); + if(event.target instanceof EntityPlayerMP || event.target instanceof EntityNPCInterface) { + AnimationData animationData = AnimationData.getData(event.target); + if (animationData.isClientAnimating()) { + AnimationData playerAnimData = AnimationData.getData(event.entityPlayer); + Animation currentAnimation = animationData.currentClientAnimation; + NBTTagCompound compound = currentAnimation.writeToNBT(); + playerAnimData.viewAnimation(currentAnimation, animationData, compound, + animationData.isClientAnimating(), currentAnimation.currentFrame, currentAnimation.currentFrameTime); + } + + if (event.target instanceof EntityNPCInterface) { + MarkData data = MarkData.get((EntityNPCInterface) event.target); + if (data.marks.isEmpty()) + return; + Server.sendData((EntityPlayerMP) event.entityPlayer, EnumPacketClient.MARK_DATA, event.target.getEntityId(), data.getNBT()); + } + } } } diff --git a/src/main/java/noppes/npcs/client/ClientCacheHandler.java b/src/main/java/noppes/npcs/client/ClientCacheHandler.java index 4314a83b8..810cc4bf0 100644 --- a/src/main/java/noppes/npcs/client/ClientCacheHandler.java +++ b/src/main/java/noppes/npcs/client/ClientCacheHandler.java @@ -1,6 +1,7 @@ package noppes.npcs.client; import net.minecraft.util.ResourceLocation; +import noppes.npcs.CommonProxy; import noppes.npcs.client.gui.OverlayQuestTracking; import noppes.npcs.client.gui.customoverlay.OverlayCustom; import noppes.npcs.client.renderer.ImageData; @@ -9,6 +10,7 @@ import noppes.npcs.controllers.data.*; import noppes.npcs.util.CacheHashMap; import java.util.HashMap; +import java.util.HashSet; import java.util.UUID; public class ClientCacheHandler { @@ -18,6 +20,7 @@ public class ClientCacheHandler { public static OverlayQuestTracking questTrackingOverlay = null; public static HashMap customOverlays = new HashMap<>(); public static HashMap> skinOverlays = new HashMap<>(); + public static HashMap playerAnimations = new HashMap<>(); public static Party party; diff --git a/src/main/java/noppes/npcs/client/ClientEventHandler.java b/src/main/java/noppes/npcs/client/ClientEventHandler.java index c42179714..69e248300 100644 --- a/src/main/java/noppes/npcs/client/ClientEventHandler.java +++ b/src/main/java/noppes/npcs/client/ClientEventHandler.java @@ -129,35 +129,9 @@ public class ClientEventHandler { ClientEventHandler.playerModel = null; } - if (data != null && data.isActive() && !Minecraft.getMinecraft().isGamePaused()) { - Animation animation = data.animation; - if (data.isActive() && animation.currentFrame().useRenderTicks()) { - animation.increaseTime(); - } - } - ClientEventHandler.renderingNpc = null; } - @SubscribeEvent - public void onUpdateEntity(LivingEvent.LivingUpdateEvent event) { - if (FMLCommonHandler.instance().getEffectiveSide() == Side.CLIENT) { - AnimationData data = null; - if (event.entity instanceof EntityNPCInterface) { - data = ((EntityNPCInterface) event.entity).display.animationData; - } else if (event.entity instanceof EntityPlayer && ClientCacheHandler.playerAnimations.containsKey(event.entity.getUniqueID())) { - data = ClientCacheHandler.playerAnimations.get(event.entity.getUniqueID()); - } - - if (data != null && data.isActive()) { - Animation animation = data.animation; - if (data.isActive() && !animation.currentFrame().useRenderTicks()) { - animation.increaseTime(); - } - } - } - } - @SubscribeEvent public void onRenderPlayer(RenderPlayerEvent.Pre event) { ClientEventHandler.renderingPlayer = event.entityPlayer; diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index b13a75387..bd80d8532 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -80,6 +80,8 @@ public class ClientProxy extends CommonProxy { public static FontContainer Font; public void load() { + this.createAnimationThread(); + Font = new FontContainer(ConfigClient.FontType, ConfigClient.FontSize); createFolders(); CustomNpcs.Channel.register(new PacketHandlerClient()); diff --git a/src/main/java/noppes/npcs/client/ClientTickHandler.java b/src/main/java/noppes/npcs/client/ClientTickHandler.java index 6424ea4ad..fc24a95dd 100644 --- a/src/main/java/noppes/npcs/client/ClientTickHandler.java +++ b/src/main/java/noppes/npcs/client/ClientTickHandler.java @@ -37,7 +37,7 @@ public class ClientTickHandler{ @SubscribeEvent(priority = EventPriority.LOWEST) public void onClientTick(TickEvent.ClientTickEvent event){ Minecraft mc = Minecraft.getMinecraft(); - if ((this.prevWorld == null || mc.theWorld == null) && this.prevWorld != mc.theWorld) { + if (mc.theWorld != null && this.prevWorld != mc.theWorld) { ClientCacheHandler.clearCache(); this.prevWorld = mc.theWorld; } diff --git a/src/main/java/noppes/npcs/client/PacketHandlerClient.java b/src/main/java/noppes/npcs/client/PacketHandlerClient.java index 07f025119..f6efe6c34 100644 --- a/src/main/java/noppes/npcs/client/PacketHandlerClient.java +++ b/src/main/java/noppes/npcs/client/PacketHandlerClient.java @@ -379,8 +379,14 @@ public class PacketHandlerClient extends PacketHandlerServer{ } animationData.setAnimation(ClientCacheHandler.animationCache.get(animationId)); - animationData.readFromNBT(compound); - Client.sendData(EnumPacketServer.CacheAnimation, animationId); + animationData.viewReadFromNBT(compound); + + if (compound.hasKey("Frame")) { + animationData.animation.readFromNBT(compound.getCompoundTag("Animation")); + animationData.animation.jumpToFrameAtTime(compound.getInteger("Frame"), compound.getInteger("Time")); + } else if (compound.hasKey("Animation")) { + Client.sendData(EnumPacketServer.CacheAnimation, animationId); + } } } else if(type == EnumPacketClient.DISABLE_MOUSE_INPUT) { diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiAnimationFrame.java b/src/main/java/noppes/npcs/client/gui/SubGuiAnimationFrame.java index 89254ce2d..ea3c7c758 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiAnimationFrame.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiAnimationFrame.java @@ -19,36 +19,42 @@ public class SubGuiAnimationFrame extends SubGuiInterface implements ITextfieldL // //customized - button, enables all the following options. this.addLabel(new GuiNpcLabel(10, "animation.customized", guiLeft + 5, guiTop + 16)); - this.addButton(new GuiNpcButton(10, guiLeft + 65, guiTop + 10, 30, 20, new String[]{"gui.yes", "gui.no"}, editingFrame.isCustomized() ? 0 : 1)); + this.addButton(new GuiNpcButton(10, guiLeft + 75, guiTop + 10, 30, 20, new String[]{"gui.yes", "gui.no"}, editingFrame.isCustomized() ? 0 : 1)); if (editingFrame.isCustomized()) { // //speed - textfield - this.addLabel(new GuiNpcLabel(11, "stats.speed", guiLeft + 5, guiTop + 36)); - this.addTextField(new GuiNpcTextField(11, this, guiLeft + 65, guiTop + 32, 30, 15, editingFrame.speed + "")); + this.addLabel(new GuiNpcLabel(11, "stats.speed", guiLeft + 5, guiTop + 42)); + this.addTextField(new GuiNpcTextField(11, this, guiLeft + 75, guiTop + 37, 30, 15, editingFrame.speed + "")); this.getTextField(11).floatsOnly = true; this.getTextField(11).setMinMaxDefaultFloat(0, Float.MAX_VALUE, 1.0F); // //smooth - button - this.addLabel(new GuiNpcLabel(12, "animation.smoothing", guiLeft + 5, guiTop + 58)); - this.addButton(new GuiNpcButton(12, guiLeft + 65, guiTop + 52, 60, 20, new String[]{"animation.smooth", "animation.linear", "gui.none"}, editingFrame.smooth)); + this.addLabel(new GuiNpcLabel(12, "animation.smoothing", guiLeft + 5, guiTop + 63)); + this.addButton(new GuiNpcButton(12, guiLeft + 75, guiTop + 55, 60, 20, new String[]{"animation.smooth", "animation.linear", "gui.none"}, editingFrame.smooth)); // - //ticks - button - this.addLabel(new GuiNpcLabel(13, "animation.tickType", guiLeft + 5, guiTop + 78)); - this.addButton(new GuiNpcButton(13, guiLeft + 65, guiTop + 72, 75, 20, new String[]{"animation.renderTicks", "animation.mcTicks"}, editingFrame.renderTicks ? 0 : 1)); + //tick duration - textfield + this.addLabel(new GuiNpcLabel(13, "animation.tickDuration", guiLeft + 5, guiTop + 84)); + this.addTextField(new GuiNpcTextField(13, this, guiLeft + 75, guiTop + 79, 40, 15, editingFrame.tickDuration + "")); + this.getTextField(13).integersOnly = true; + this.getTextField(13).setMinMaxDefault(1, Integer.MAX_VALUE, 50); } + + this.addLabel(new GuiNpcLabel(14, "Comment", guiLeft + 5, guiTop + 106)); + this.addTextField(new GuiNpcTextField(14, this, guiLeft + 50, guiTop + 101, 120, 15, editingFrame.getComment())); } @Override protected void actionPerformed(GuiButton guibutton) { super.actionPerformed(guibutton); - int value = ((GuiNpcButton)guibutton).getValue(); + if (this.editingFrame == null) { + return; + } - if (guibutton.id == 10 && editingFrame != null) { + int value = ((GuiNpcButton)guibutton).getValue(); + if (guibutton.id == 10) { editingFrame.setCustomized(!editingFrame.isCustomized()); - } else if (guibutton.id == 12 && editingFrame != null) { + } else if (guibutton.id == 12) { editingFrame.smooth = (byte) value; - } else if (guibutton.id == 13 && editingFrame != null) { - editingFrame.renderTicks = value == 0; } initGui(); @@ -56,8 +62,16 @@ public class SubGuiAnimationFrame extends SubGuiInterface implements ITextfieldL @Override public void unFocused(GuiNpcTextField textfield) { - if (textfield.id == 11 && this.editingFrame != null) { + if (this.editingFrame == null) { + return; + } + + if (textfield.id == 11) { this.editingFrame.speed = textfield.getFloat(); + } else if (textfield.id == 13) { + this.editingFrame.tickDuration = textfield.getInteger(); + } else if (textfield.id == 14) { + this.editingFrame.setComment(textfield.getText()); } } } diff --git a/src/main/java/noppes/npcs/client/gui/SubGuiAnimationOptions.java b/src/main/java/noppes/npcs/client/gui/SubGuiAnimationOptions.java index 5a021a406..a4d4f79ca 100644 --- a/src/main/java/noppes/npcs/client/gui/SubGuiAnimationOptions.java +++ b/src/main/java/noppes/npcs/client/gui/SubGuiAnimationOptions.java @@ -1,12 +1,10 @@ package noppes.npcs.client.gui; import net.minecraft.client.gui.GuiButton; -import noppes.npcs.client.gui.util.GuiNpcButton; -import noppes.npcs.client.gui.util.GuiNpcLabel; -import noppes.npcs.client.gui.util.SubGuiInterface; +import noppes.npcs.client.gui.util.*; import noppes.npcs.controllers.data.Animation; -public class SubGuiAnimationOptions extends SubGuiInterface { +public class SubGuiAnimationOptions extends SubGuiInterface implements ITextfieldListener { private final Animation animation; public SubGuiAnimationOptions(Animation animation) { @@ -20,8 +18,10 @@ public class SubGuiAnimationOptions extends SubGuiInterface { super.initGui(); // //ticks - button - this.addLabel(new GuiNpcLabel(10, "animation.tickType", guiLeft + 5, guiTop + 16)); - this.addButton(new GuiNpcButton(10, guiLeft + 60, guiTop + 10, 75, 20, new String[]{"animation.renderTicks", "animation.mcTicks"}, animation.renderTicks ? 0 : 1)); + this.addLabel(new GuiNpcLabel(10, "animation.tickDuration", guiLeft + 5, guiTop + 16)); + this.addTextField(new GuiNpcTextField(10, this, guiLeft + 80, guiTop + 12, 40, 15, animation.tickDuration + "")); + this.getTextField(10).integersOnly = true; + this.getTextField(10).setMinMaxDefault(1, Integer.MAX_VALUE, 50); // //whileStanding - button this.addLabel(new GuiNpcLabel(11, "animation.whileStanding", guiLeft + 5, guiTop + 36)); @@ -41,9 +41,7 @@ public class SubGuiAnimationOptions extends SubGuiInterface { super.actionPerformed(guibutton); int value = ((GuiNpcButton)guibutton).getValue(); - if (guibutton.id == 10) { - animation.renderTicks = value == 0; - } else if (guibutton.id == 11) { + if (guibutton.id == 11) { animation.whileStanding = value == 0; } else if (guibutton.id == 12) { animation.whileAttacking = value == 0; @@ -53,4 +51,11 @@ public class SubGuiAnimationOptions extends SubGuiInterface { initGui(); } + + @Override + public void unFocused(GuiNpcTextField textfield) { + if (textfield.id == 10) { + animation.tickDuration = textfield.getInteger(); + } + } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNPCEditAnimation.java b/src/main/java/noppes/npcs/client/gui/global/GuiNPCEditAnimation.java index 70c3a9470..6991e54be 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNPCEditAnimation.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNPCEditAnimation.java @@ -23,14 +23,16 @@ import java.util.Map; public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfieldListener, ISliderListener, ISubGuiListener { private final Animation animation; + private static int partEditMode; + private static int sliderSelection = 0; + private static Frame copiedFrame; + private EnumAnimationPart editingPart = EnumAnimationPart.HEAD; private int frameIndex = 0; public boolean playingAnimation = false; private Frame prevFrame; private final GuiScreen parent; private long prevTick; - private static int partEditMode; - private static int sliderSelection = 0; private final GuiNpcSlider[] rotationSliders = new GuiNpcSlider[3]; private final GuiNpcSlider[] pivotSliders = new GuiNpcSlider[3]; @@ -49,7 +51,7 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield this.animation = animation; AnimationData data = npc.display.animationData; - data.animation = animation; + data.setAnimation(animation); data.setEnabled(true); int bodyPartX = 280; @@ -92,9 +94,9 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield AnimationData data = npc.display.animationData; if (!playingAnimation) { - data.animation = new Animation(); + data.setAnimation(new Animation()); data.animation.smooth = animation.smooth; - data.animation.renderTicks = animation.renderTicks; + data.animation.tickDuration = 50; data.animation.loop = 0; if (editingFrame != null) { data.animation.frames.add(editingFrame); @@ -107,7 +109,7 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield if (!frame.isCustomized()) { frame.speed = animation.speed; frame.smooth = animation.smooth; - frame.renderTicks = animation.renderTicks; + frame.tickDuration = animation.tickDuration(); } for (Map.Entry entry : frame.frameParts.entrySet()) { FramePart part = entry.getValue(); @@ -401,13 +403,7 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield AnimationData data = npc.display.animationData; if (guibutton.id == 11) { - if (frameIndex < animation.frames.size() - 1) { - animation.frames.add(frameIndex + 1, new Frame(10)); - } else { - animation.frames.add(new Frame(10)); - } - this.frameIndex = frameIndex + 1; - updateFrameSlider(); + this.addFrame(); } else if (guibutton.id == 12) { animation.frames.remove(frameIndex); updateFrameSlider(); @@ -475,7 +471,7 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield } } this.playingAnimation = true; - data.animation = animation; + data.setAnimation(this.animation); data.animation.paused = false; } else if (guibutton.id == 201) { data.animation.paused = true; @@ -501,6 +497,20 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield initGui(); } + private void addFrame() { + this.addFrame(new Frame(10)); + } + + private void addFrame(Frame frame) { + if (frameIndex < animation.frames.size() - 1) { + animation.frames.add(frameIndex + 1, frame); + } else { + animation.frames.add(frame); + } + this.frameIndex = frameIndex + 1; + updateFrameSlider(); + } + @Override public void drawScreen(int par1, int par2, float par3) { @@ -510,12 +520,8 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield this.playingAnimation = false; initGui(); } else if (data.isActive()) { - Frame currentFrame = (Frame) data.animation.currentFrame(); long time = mc.theWorld.getTotalWorldTime(); if (time != prevTick) { - if (currentFrame != null && !currentFrame.renderTicks) { - data.animation.increaseTime(); - } GuiNpcLabel label = this.getLabel(213); if (label != null) { label.label += "."; @@ -643,4 +649,28 @@ public class GuiNPCEditAnimation extends GuiModelInterface implements ITextfield editingFrame.setColorMarker(((SubGuiColorSelector) subgui).color); } } + + @Override + public void keyTyped(char par1, int par2) { + super.keyTyped(par1, par2); + + if (GuiScreen.isCtrlKeyDown() && par2 != 29 && !GuiNpcTextField.isFieldActive()) { + switch (par2) { + case 46: + if (this.editingFrame() != null) { + copiedFrame = this.editingFrame(); + } + break; + case 47: + if (copiedFrame != null) { + Frame frame = new Frame(10); + frame.parent = this.animation; + frame.readFromNBT(copiedFrame.writeToNBT()); + this.addFrame(frame); + this.initGui(); + } + break; + } + } + } } diff --git a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageAnimations.java b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageAnimations.java index cbcdd0ed1..e5b5d07a5 100644 --- a/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageAnimations.java +++ b/src/main/java/noppes/npcs/client/gui/global/GuiNPCManageAnimations.java @@ -63,9 +63,9 @@ public class GuiNPCManageAnimations extends GuiModelInterface2 implements IScrol AnimationData data = npc.display.animationData; if (!playingAnimation) { - data.animation = new Animation(); + data.setAnimation(new Animation()); data.animation.smooth = animation.smooth; - data.animation.renderTicks = animation.renderTicks; + data.animation.tickDuration = 50; data.animation.loop = 0; if (this.animation.frames.size() > 0) { @@ -109,12 +109,8 @@ public class GuiNPCManageAnimations extends GuiModelInterface2 implements IScrol this.playingAnimation = false; initGui(); } else if (data.isActive()) { - Frame currentFrame = (Frame) data.animation.currentFrame(); long time = mc.theWorld.getTotalWorldTime(); if (time != prevTick) { - if (currentFrame != null && !currentFrame.renderTicks) { - data.animation.increaseTime(); - } GuiNpcLabel label = this.getLabel(94); if (label != null) { label.label += "."; @@ -186,7 +182,7 @@ public class GuiNPCManageAnimations extends GuiModelInterface2 implements IScrol } } this.playingAnimation = true; - data.animation = animation; + data.setAnimation(this.animation); data.animation.paused = false; } else if (guibutton.id == 92) { data.animation.paused = true; @@ -207,7 +203,7 @@ public class GuiNPCManageAnimations extends GuiModelInterface2 implements IScrol this.animation = new Animation(); animation.readFromNBT(compound); setSelected(animation.name); - npc.display.animationData.animation = animation; + npc.display.animationData.setAnimation(this.animation); this.playingAnimation = false; npc.display.animationData.animation.paused = false; initGui(); diff --git a/src/main/java/noppes/npcs/constants/EnumScriptType.java b/src/main/java/noppes/npcs/constants/EnumScriptType.java index a8f233f47..fd968a716 100644 --- a/src/main/java/noppes/npcs/constants/EnumScriptType.java +++ b/src/main/java/noppes/npcs/constants/EnumScriptType.java @@ -99,13 +99,27 @@ public enum EnumScriptType { PARTY_INVITE("partyInvite"), PARTY_KICK("partyKick"), PARTY_LEAVE("partyLeave"), - PARTY_DISBAND("partyDisband"); + PARTY_DISBAND("partyDisband"), + + ANIMATION_START("animationStart"), + ANIMATION_END("animationEnd"), + ANIMATION_FRAME_ENTER("frameEnter"), + ANIMATION_FRAME_EXIT("frameExit"); public String function; public static EnumScriptType valueOfIgnoreCase(String channelName) { channelName = channelName.toUpperCase(); - return valueOf(channelName); + try { + return valueOf(channelName); + } catch (IllegalArgumentException exception) { + for (EnumScriptType value : EnumScriptType.values()) { + if (value.function.toUpperCase().equals(channelName)) { + return value; + } + } + throw exception; + } } private EnumScriptType(String function) { diff --git a/src/main/java/noppes/npcs/controllers/data/Animation.java b/src/main/java/noppes/npcs/controllers/data/Animation.java index 569ff7614..be05ef4e1 100644 --- a/src/main/java/noppes/npcs/controllers/data/Animation.java +++ b/src/main/java/noppes/npcs/controllers/data/Animation.java @@ -1,10 +1,15 @@ package noppes.npcs.controllers.data; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import noppes.npcs.CustomNpcs; +import noppes.npcs.DataDisplay; +import noppes.npcs.EventHooks; import noppes.npcs.api.handler.data.IAnimation; +import noppes.npcs.api.handler.data.IAnimationData; import noppes.npcs.api.handler.data.IFrame; import noppes.npcs.constants.EnumAnimationPart; import noppes.npcs.controllers.AnimationController; @@ -24,7 +29,7 @@ public class Animation implements IAnimation { public float speed = 1.0F; public byte smooth = 0; public int loop = -1; //If greater than 0 and less than the amount of frames, the animation will begin looping when it reaches this frame. - public boolean renderTicks = false; // If true, MC ticks are used. If false, render ticks are used. + public int tickDuration = 50; public boolean whileStanding = true; public boolean whileAttacking = true; @@ -48,6 +53,10 @@ public class Animation implements IAnimation { this.id = id; } + public IAnimationData getParent() { + return this.parent; + } + public IFrame currentFrame() { return currentFrame < frames.size() ? frames.get(currentFrame) : null; } @@ -93,6 +102,15 @@ public class Animation implements IAnimation { return this.name; } + public int tickDuration() { + return this.tickDuration; + } + + public IAnimation setTickDuration(int tickDuration) { + this.tickDuration = tickDuration; + return this; + } + public IAnimation setSpeed(float speed) { this.speed = speed; return this; @@ -111,15 +129,6 @@ public class Animation implements IAnimation { return this.smooth; } - public IAnimation useRenderTicks(boolean renderTicks) { - this.renderTicks = renderTicks; - return this; - } - - public boolean useRenderTicks() { - return this.renderTicks; - } - public IAnimation doWhileStanding(boolean whileStanding) { this.whileStanding = whileStanding; return this; @@ -170,6 +179,14 @@ public class Animation implements IAnimation { id = newID; } + public long getTotalTime() { + long time = 0; + for (Frame frame : this.frames) { + time += (long) frame.duration * (frame.customized ? frame.tickDuration : this.tickDuration); + } + return time; + } + public void readFromNBT(NBTTagCompound compound){ if(compound.hasKey("ID")){ id = compound.getInteger("ID"); @@ -183,7 +200,11 @@ public class Animation implements IAnimation { smooth = compound.getByte("Smooth"); loop = compound.getInteger("Loop"); - renderTicks = compound.getBoolean("RenderTicks"); + if (compound.hasKey("TickDuration")) { + this.tickDuration = compound.getInteger("TickDuration"); + } else if(compound.hasKey("RenderTicks")){ + this.tickDuration = compound.getBoolean("RenderTicks") ? 20 : 50; + } ArrayList frames = new ArrayList(); NBTTagList list = compound.getTagList("Frames", 10); @@ -199,6 +220,9 @@ public class Animation implements IAnimation { this.whileStanding = compound.getBoolean("WhileStanding"); this.whileMoving = compound.getBoolean("WhileWalking"); this.whileAttacking = compound.getBoolean("WhileAttacking"); + + this.currentFrame = compound.getInteger("CurrentFrame"); + this.currentFrameTime = compound.getInteger("CurrentFrameTime"); } public NBTTagCompound writeToNBT(){ @@ -208,8 +232,7 @@ public class Animation implements IAnimation { compound.setFloat("Speed", speed); compound.setByte("Smooth", smooth); compound.setInteger("Loop",loop); - - compound.setBoolean("RenderTicks", renderTicks); + compound.setInteger("TickDuration", tickDuration); NBTTagList list = new NBTTagList(); for(Frame frame : frames){ @@ -221,20 +244,24 @@ public class Animation implements IAnimation { compound.setBoolean("WhileStanding", whileStanding); compound.setBoolean("WhileWalking", whileMoving); compound.setBoolean("WhileAttacking", whileAttacking); + + compound.setInteger("CurrentFrame", currentFrame); + compound.setInteger("CurrentFrameTime", currentFrameTime); return compound; } - @SideOnly(Side.CLIENT) - public void increaseTime() { + public boolean increaseTime() { if (paused) - return; + return false; if (this.parent != null && this.currentFrame < this.frames.size()) { this.parent.finishedFrame = this.currentFrame; } this.currentFrameTime++; - if (this.currentFrameTime == this.currentFrame().getDuration()) { + if (this.currentFrame() != null && this.currentFrameTime == this.currentFrame().getDuration()) { + EventHooks.onAnimationFrameExited(this, this.currentFrame()); + Frame prevFrame = (Frame) this.currentFrame(); Frame nextFrame = null; this.currentFrameTime = 0; @@ -244,6 +271,13 @@ public class Animation implements IAnimation { } else if (this.loop >= 0 && this.loop < this.frames.size()) { this.currentFrame = this.loop; } + + if (this.currentFrame() != null) { + EventHooks.onAnimationFrameEntered(this, this.currentFrame()); + } else { + EventHooks.onAnimationEnded(this); + } + if (nextFrame != null) { for (EnumAnimationPart part : EnumAnimationPart.values()) { if (prevFrame.frameParts.containsKey(part) && nextFrame.frameParts.containsKey(part)) { @@ -252,23 +286,32 @@ public class Animation implements IAnimation { } } } else if (this.parent != null && this.currentFrame > this.loop) { - this.parent.finishedTime = this.parent.animationEntity.getAge(); + this.parent.finishedTime = this.parent.getMCEntity().getAge(); } } + + return true; } @SideOnly(Side.CLIENT) public void jumpToCurrentFrame() { - this.currentFrameTime = 0; - Frame frame = (Frame) this.currentFrame(); - if(frame != null){ - for (EnumAnimationPart part : EnumAnimationPart.values()) { - if(part != null){ - if (frame.frameParts.containsKey(part)) { - frame.frameParts.get(part).jumpToCurrentFrame(); - } - } - } - } + this.jumpToFrameAtTime(this.currentFrame, 0); } + + @SideOnly(Side.CLIENT) + public void jumpToFrameAtTime(int frameIndex, int time) { + this.currentFrame = frameIndex; + this.currentFrameTime = time; + + Frame frame = (Frame) this.currentFrame(); + if(frame != null){ + for (EnumAnimationPart part : EnumAnimationPart.values()) { + if(part != null){ + if (frame.frameParts.containsKey(part)) { + frame.frameParts.get(part).jumpToCurrentFrame(); + } + } + } + } + } } diff --git a/src/main/java/noppes/npcs/controllers/data/AnimationData.java b/src/main/java/noppes/npcs/controllers/data/AnimationData.java index 37528ce95..a7d120672 100644 --- a/src/main/java/noppes/npcs/controllers/data/AnimationData.java +++ b/src/main/java/noppes/npcs/controllers/data/AnimationData.java @@ -1,21 +1,24 @@ package noppes.npcs.controllers.data; -import cpw.mods.fml.common.FMLCommonHandler; -import cpw.mods.fml.relauncher.Side; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.AxisAlignedBB; -import noppes.npcs.DataDisplay; -import noppes.npcs.Server; +import noppes.npcs.*; +import noppes.npcs.api.entity.IAnimatable; +import noppes.npcs.api.entity.ICustomNpc; +import noppes.npcs.api.entity.IEntity; +import noppes.npcs.api.entity.IPlayer; import noppes.npcs.api.handler.data.IAnimation; import noppes.npcs.api.handler.data.IAnimationData; import noppes.npcs.constants.EnumAnimationPart; import noppes.npcs.constants.EnumPacketClient; import noppes.npcs.controllers.PlayerDataController; import noppes.npcs.entity.EntityNPCInterface; +import noppes.npcs.scripted.NpcAPI; import java.util.Arrays; import java.util.HashSet; @@ -29,20 +32,48 @@ public class AnimationData implements IAnimationData { public Animation animation; public boolean allowAnimation = false; + public long animatingTime = 0; + + public Animation currentClientAnimation; + private boolean isClientAnimating; private final HashSet cachedAnimationIDs = new HashSet<>(); //Client-side values - public EntityLivingBase animationEntity; public int finishedTime = -1; public int finishedFrame = -1; public AnimationData(Object parent){ this.parent = parent; - if (parent instanceof DataDisplay) { - this.animationEntity = ((DataDisplay) parent).npc; - } else if (parent instanceof EntityPlayer) { - this.animationEntity = (EntityLivingBase) parent; + } + + public static AnimationData getData(Entity entity) { + if (entity instanceof EntityPlayerMP) { + return PlayerData.get((EntityPlayer) entity).animationData; + } else if (entity instanceof EntityNPCInterface) { + return ((EntityNPCInterface)entity).display.animationData; + } else { + return null; + } + } + + public IAnimatable getEntity() { + IEntity entity = NpcAPI.Instance().getIEntity(this.getMCEntity()); + if (entity instanceof IAnimatable) { + return (IAnimatable) entity; + } + return null; + } + + public EntityLivingBase getMCEntity() { + if (this.parent instanceof DataDisplay) { + return ((DataDisplay) this.parent).npc; + } else { + if (this.parent instanceof PlayerData) { + return ((PlayerData) this.parent).player; + } else { + return (EntityPlayer) this.parent; + } } } @@ -57,6 +88,30 @@ public class AnimationData implements IAnimationData { if(sendingEntity.dimension != sendingEntity.worldObj.provider.dimensionId) sendingEntity.dimension = sendingEntity.worldObj.provider.dimensionId; + this.animatingTime = 0; + + boolean prevIsClientAnimating = this.isClientAnimating && this.currentClientAnimation.currentFrame() != null; + this.isClientAnimating = this.allowAnimation && this.animation != null; + if (prevIsClientAnimating && (!this.isClientAnimating || this.animation != this.currentClientAnimation)) { + EventHooks.onAnimationEnded(this.currentClientAnimation); + } + if (this.isClientAnimating) { + this.currentClientAnimation = this.animation; + } + + if (this.animation != null && this.allowAnimation) { + if (EventHooks.onAnimationStarted(this.animation)) + return; + EventHooks.onAnimationFrameEntered(this.animation, this.animation.currentFrame()); + } + + synchronized (CommonProxy.serverPlayingAnimations) { + if (this.isClientAnimating && this.getMCEntity() != null && + !this.getMCEntity().worldObj.isRemote && this.animation != null) { + CommonProxy.serverPlayingAnimations.add(this); + } + } + List entities = sendingEntity.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getBoundingBox( sendingEntity.posX - range, sendingEntity.posY - range, sendingEntity.posZ - range, sendingEntity.posX + range, sendingEntity.posY + range, sendingEntity.posZ + range)); @@ -69,13 +124,21 @@ public class AnimationData implements IAnimationData { if (animationNBT == null && this.animation != null && !animationData.isCached(this.animation.getID())) { animationNBT = this.animation.writeToNBT(); } - animationData.viewAnimation(this.animation, sendingEntity, this, animationNBT); + animationData.viewAnimation(this.animation, this, animationNBT); } } } + public boolean isClientAnimating() { + return this.isClientAnimating; + } + public boolean isActive() { - if (!this.allowAnimation || this.animation == null || this.animation.currentFrame == this.animation.frames.size() || animation.currentFrame() == null) + return this.isActive(this.animation); + } + + public boolean isActive(Animation animation) { + if (!this.allowAnimation || animation == null || animation.currentFrame == animation.frames.size() || animation.currentFrame() == null) return false; if (this.parent instanceof DataDisplay) { @@ -100,6 +163,22 @@ public class AnimationData implements IAnimationData { } } + public void increaseTime() { + Animation updateAnimation = null; + if (this.isActive()) { + updateAnimation = this.animation; + } else if (this.isActive(animation.parent.currentClientAnimation)) { + updateAnimation = this.currentClientAnimation; + } + + if (updateAnimation != null && updateAnimation.increaseTime()) { + Frame frame = (Frame) updateAnimation.currentFrame(); + if (frame != null) { + this.animatingTime += frame.customized ? frame.tickDuration : updateAnimation.tickDuration; + } + } + } + public boolean isCached(int id) { return this.cachedAnimationIDs.contains(id); } @@ -116,27 +195,63 @@ public class AnimationData implements IAnimationData { this.cachedAnimationIDs.clear(); } - public void viewAnimation(Animation animation, EntityLivingBase entity, AnimationData animationData, NBTTagCompound animationNBT) { - NBTTagCompound data = animationData.writeToNBT(new NBTTagCompound()); + public void viewAnimation(Animation animation, AnimationData animationData, NBTTagCompound animationNBT) { + this.viewAnimation(animation, animationData, animationNBT, animationData.allowAnimation, -1, -1); + } + + public void viewAnimation(Animation animation, AnimationData animationData, NBTTagCompound animationNBT, boolean enabled, int currentFrame, int time) { + boolean prevEnabled = animationData.allowAnimation; + animationData.allowAnimation = enabled; + NBTTagCompound data = animationData.viewWriteNBT(new NBTTagCompound()); + animationData.allowAnimation = prevEnabled; + + if (animation != null && currentFrame >= 0 && currentFrame < animation.frames.size()) { + data.setInteger("Frame", currentFrame); + data.setInteger("Time", time); + } + if (animation != null) { - if (this.cachedAnimationIDs.contains(animation.getID())) { + if (this.cachedAnimationIDs.contains(animation.getID()) && !data.hasKey("Frame")) { data.setInteger("AnimationID", animation.getID()); } else { data.setTag("Animation", animationNBT); } } + + IAnimatable animatable = animationData.getEntity(); + Entity entity = ((IEntity) animatable).getMCEntity(); if (!(entity instanceof EntityPlayer)) { data.setInteger("EntityId", entity.getEntityId()); } + Server.sendData((EntityPlayerMP) ((PlayerData) parent).player, EnumPacketClient.UPDATE_ANIMATIONS, data, entity.getCommandSenderName()); } + public NBTTagCompound viewWriteNBT(NBTTagCompound compound) { + compound.setBoolean("AllowAnimation", allowAnimation); + return compound; + } + + public void viewReadFromNBT(NBTTagCompound compound) { + this.setEnabled(compound.getBoolean("AllowAnimation")); + } + public NBTTagCompound writeToNBT(NBTTagCompound compound) { + if (this.currentClientAnimation != null) { + compound.setTag("CurrentAnimation", currentClientAnimation.writeToNBT()); + compound.setBoolean("IsClientAnimating", isClientAnimating); + } compound.setBoolean("AllowAnimation", allowAnimation); return compound; } public void readFromNBT(NBTTagCompound compound) { + if (compound.hasKey("CurrentAnimation")) { + this.currentClientAnimation = new Animation(); + this.currentClientAnimation.parent = this; + this.currentClientAnimation.readFromNBT(compound.getCompoundTag("CurrentAnimation")); + this.isClientAnimating = compound.getBoolean("IsClientAnimating"); + } this.setEnabled(compound.getBoolean("AllowAnimation")); } @@ -154,33 +269,50 @@ public class AnimationData implements IAnimationData { } public void setAnimation(IAnimation animation) { - if (FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER) { - this.animation = (Animation) animation; - } else { - Animation newAnim = new Animation(); + Animation newAnim = null; + if (animation != null) { + newAnim = new Animation(); newAnim.readFromNBT(((Animation) animation).writeToNBT()); - if (this.isActive() && !newAnim.frames.isEmpty()) { - Frame frame = (Frame) this.animation.currentFrame(); - if (frame != null) { - Frame firstFrame = newAnim.frames.get(0); - for (Map.Entry entry : frame.frameParts.entrySet()) { - if (firstFrame.frameParts.containsKey(entry.getKey())) { - FramePart prevFramePart = entry.getValue(); - FramePart newFramePart = firstFrame.frameParts.get(entry.getKey()); - for (int i = 0; i < 3; i++) { - newFramePart.prevPivots[i] = prevFramePart.prevPivots[i]; - newFramePart.prevRotations[i] = prevFramePart.prevRotations[i]; - } + newAnim.currentFrame = 0; + newAnim.currentFrameTime = 0; + newAnim.parent = this; + } + + + if (this.getMCEntity() != null && this.getMCEntity().worldObj.isRemote && newAnim != null) { + this.animatingTime = 0; + synchronized (CommonProxy.clientPlayingAnimations) { + CommonProxy.clientPlayingAnimations.add(this); + } + } + + Animation prevAnim = this.animation; + this.animation = newAnim; + + if (this.getMCEntity() != null && this.getMCEntity().worldObj.isRemote && + this.isActive() && prevAnim != null && newAnim != null && !newAnim.frames.isEmpty()) { + Frame frame = (Frame) prevAnim.currentFrame(); + if (frame != null) { + Frame firstFrame = newAnim.frames.get(0); + for (Map.Entry entry : frame.frameParts.entrySet()) { + if (firstFrame.frameParts.containsKey(entry.getKey())) { + FramePart prevFramePart = entry.getValue(); + FramePart newFramePart = firstFrame.frameParts.get(entry.getKey()); + for (int i = 0; i < 3; i++) { + newFramePart.prevPivots[i] = prevFramePart.prevPivots[i]; + newFramePart.prevRotations[i] = prevFramePart.prevRotations[i]; } } } } - this.animation = newAnim; - newAnim.parent = this; } } public IAnimation getAnimation() { return animation; } + + public long getAnimatingTime() { + return this.animatingTime; + } } diff --git a/src/main/java/noppes/npcs/controllers/data/DataScript.java b/src/main/java/noppes/npcs/controllers/data/DataScript.java index d1530f63d..d567f11d2 100644 --- a/src/main/java/noppes/npcs/controllers/data/DataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/DataScript.java @@ -236,13 +236,14 @@ public class DataScript implements INpcScriptHandler { @Override public void callScript(EnumScriptType type, Event event) { + ScriptController.Instance.globalNpcScripts.callScript(type, event); callScript(type, event, "$$IGNORED$$", null); } @Override public void callScript(String hookName, Event event) { try { - EnumScriptType enumScriptType = EnumScriptType.valueOf(hookName); + EnumScriptType enumScriptType = EnumScriptType.valueOfIgnoreCase(hookName); this.callScript(enumScriptType, event); } catch (IllegalArgumentException ignored) {} } diff --git a/src/main/java/noppes/npcs/controllers/data/Frame.java b/src/main/java/noppes/npcs/controllers/data/Frame.java index df80096bd..004d80c28 100644 --- a/src/main/java/noppes/npcs/controllers/data/Frame.java +++ b/src/main/java/noppes/npcs/controllers/data/Frame.java @@ -14,12 +14,14 @@ public class Frame implements IFrame { public Animation parent; public HashMap frameParts = new HashMap<>(); public int duration = 0; + public int tickDuration = 50; + boolean customized = false; public float speed = 1.0F; public byte smooth = 0; - public boolean renderTicks = false; // If true, MC ticks are used. If false, render ticks are used. private int colorMarker = 0xFFFFFF; + private String comment = ""; public Frame(){} @@ -75,6 +77,15 @@ public class Frame implements IFrame { return this; } + public int tickDuration() { + return !this.customized && this.parent != null ? this.parent.tickDuration : this.tickDuration; + } + + public IFrame setTickDuration(int tickDuration) { + this.tickDuration = tickDuration; + return this; + } + public boolean isCustomized() { return customized; } @@ -102,15 +113,6 @@ public class Frame implements IFrame { return this; } - public IFrame useRenderTicks(boolean renderTicks) { - this.renderTicks = renderTicks; - return this; - } - - public boolean useRenderTicks() { - return this.renderTicks; - } - public int getColorMarker() { return this.colorMarker; } @@ -119,11 +121,22 @@ public class Frame implements IFrame { this.colorMarker = color; } + public String getComment() { + return this.comment; + } + + public void setComment(String comment) { + this.comment = comment; + } + public void readFromNBT(NBTTagCompound compound){ duration = compound.getInteger("Duration"); if (compound.hasKey("ColorMarker")) { this.setColorMarker(compound.getInteger("ColorMarker")); } + if (compound.hasKey("Comment")) { + this.comment = compound.getString("Comment"); + } // Customized = TRUE if Speed or Smooth Exist if(compound.hasKey("Speed")){ @@ -134,15 +147,19 @@ public class Frame implements IFrame { customized = true; smooth = compound.getByte("Smooth"); } - if(compound.hasKey("RenderTicks")){ + + if (compound.hasKey("TickDuration")) { + customized = true; + this.tickDuration = compound.getInteger("TickDuration"); + } else if(compound.hasKey("RenderTicks")){ customized = true; - renderTicks = compound.getBoolean("RenderTicks"); + this.tickDuration = compound.getBoolean("RenderTicks") ? 20 : 50; } - if (!customized) { + if (!customized && parent != null) { this.speed = parent.speed; this.smooth = parent.smooth; - this.renderTicks = parent.renderTicks; + this.tickDuration = parent.tickDuration; } HashMap frameParts = new HashMap<>(); @@ -164,11 +181,12 @@ public class Frame implements IFrame { NBTTagCompound compound = new NBTTagCompound(); compound.setInteger("Duration", duration); compound.setInteger("ColorMarker", this.colorMarker); + compound.setString("Comment", this.comment); if(customized){ compound.setFloat("Speed", speed); compound.setByte("Smooth", smooth); - compound.setBoolean("RenderTicks", renderTicks); + compound.setInteger("TickDuration", tickDuration); } NBTTagList list = new NBTTagList(); @@ -191,7 +209,7 @@ public class Frame implements IFrame { frame.customized = this.customized; frame.speed = this.speed; frame.smooth = this.smooth; - frame.renderTicks = this.renderTicks; + frame.tickDuration = this.tickDuration; frame.colorMarker = this.colorMarker; return frame; } diff --git a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java index 85096dfe0..dbd618fa5 100644 --- a/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java +++ b/src/main/java/noppes/npcs/controllers/data/PlayerDataScript.java @@ -1,6 +1,7 @@ package noppes.npcs.controllers.data; import com.google.common.base.Preconditions; +import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.eventhandler.Event; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.nbt.NBTTagCompound; @@ -97,7 +98,7 @@ public class PlayerDataScript implements INpcScriptHandler { } public boolean isClient() { - return this.player.isClientWorld(); + return false; } public boolean getEnabled() { return ScriptController.Instance.playerScripts.enabled; diff --git a/src/main/java/noppes/npcs/scripted/event/AnimationEvent.java b/src/main/java/noppes/npcs/scripted/event/AnimationEvent.java new file mode 100644 index 000000000..1690f8e57 --- /dev/null +++ b/src/main/java/noppes/npcs/scripted/event/AnimationEvent.java @@ -0,0 +1,108 @@ +package noppes.npcs.scripted.event; + +import cpw.mods.fml.common.eventhandler.Cancelable; +import noppes.npcs.api.entity.IAnimatable; +import noppes.npcs.api.event.IAnimationEvent; +import noppes.npcs.api.handler.data.IAnimation; +import noppes.npcs.api.handler.data.IAnimationData; +import noppes.npcs.api.handler.data.IFrame; +import noppes.npcs.constants.EnumScriptType; +import noppes.npcs.controllers.data.Animation; +import noppes.npcs.controllers.data.Frame; + +public abstract class AnimationEvent extends CustomNPCsEvent implements IAnimationEvent { + protected final IAnimation animation; + protected final IAnimationData animationData; + protected final IAnimatable entity; + + public AnimationEvent(IAnimation animation) { + this.animation = animation; + this.animationData = animation.getParent(); + this.entity = this.animationData.getEntity(); + } + + @Override + public IAnimation getAnimation() { + return this.animation; + } + + @Override + public IAnimationData getAnimationData() { + return this.animationData; + } + + @Override + public IAnimatable getEntity() { + return this.entity; + } + + @Cancelable + public static class Started extends AnimationEvent { + + public Started(IAnimation animation) { + super(animation); + } + + @Override + public String getHookName() { + return EnumScriptType.ANIMATION_START.function; + } + } + + public static class Ended extends AnimationEvent { + + public Ended(IAnimation animation) { + super(animation); + } + + @Override + public String getHookName() { + return EnumScriptType.ANIMATION_END.function; + } + } + + public static abstract class FrameEvent extends AnimationEvent implements IFrameEvent { + private final IFrame frame; + private final int index; + + public FrameEvent(IAnimation animation, IFrame frame) { + super(animation); + this.frame = frame; + this.index = ((Animation)this.getAnimation()).frames.indexOf((Frame) this.frame); + } + + @Override + public int getIndex() { + return this.index; + } + + @Override + public IFrame getFrame() { + return this.frame; + } + + public static class Entered extends FrameEvent { + + public Entered(IAnimation animation, IFrame frame) { + super(animation, frame); + } + + @Override + public String getHookName() { + return EnumScriptType.ANIMATION_FRAME_ENTER.function; + } + } + + public static class Exited extends FrameEvent { + + public Exited(IAnimation animation, IFrame frame) { + super(animation, frame); + } + + @Override + public String getHookName() { + return EnumScriptType.ANIMATION_FRAME_EXIT.function; + } + } + } +} diff --git a/src/main/java/noppes/npcs/scripted/event/BlockEvent.java b/src/main/java/noppes/npcs/scripted/event/BlockEvent.java index 3367a1699..48a03ec9e 100644 --- a/src/main/java/noppes/npcs/scripted/event/BlockEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/BlockEvent.java @@ -11,7 +11,7 @@ import noppes.npcs.api.event.IBlockEvent; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.scripted.NpcAPI; -public class BlockEvent extends CustomNPCsEvent implements IBlockEvent { +public abstract class BlockEvent extends CustomNPCsEvent implements IBlockEvent { public IBlock block; public BlockEvent(IBlock block){ this.block = block; diff --git a/src/main/java/noppes/npcs/scripted/event/CustomGuiEvent.java b/src/main/java/noppes/npcs/scripted/event/CustomGuiEvent.java index 130a6657c..a62c02156 100644 --- a/src/main/java/noppes/npcs/scripted/event/CustomGuiEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/CustomGuiEvent.java @@ -10,7 +10,7 @@ import noppes.npcs.api.item.IItemStack; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.scripted.NpcAPI; -public class CustomGuiEvent extends CustomNPCsEvent implements ICustomGuiEvent { +public abstract class CustomGuiEvent extends CustomNPCsEvent implements ICustomGuiEvent { public final IPlayer player; public final ICustomGui gui; diff --git a/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java b/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java index 1f95ec760..b74e3d418 100644 --- a/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/CustomNPCsEvent.java @@ -12,12 +12,10 @@ import noppes.npcs.api.handler.data.INaturalSpawn; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.scripted.NpcAPI; -public class CustomNPCsEvent extends Event implements ICustomNPCsEvent { +public abstract class CustomNPCsEvent extends Event implements ICustomNPCsEvent { public final AbstractNpcAPI API = AbstractNpcAPI.Instance(); - public String getHookName() { - return "CNPCEvent"; - } + public abstract String getHookName(); public CustomNPCsEvent() { } diff --git a/src/main/java/noppes/npcs/scripted/event/DialogEvent.java b/src/main/java/noppes/npcs/scripted/event/DialogEvent.java index 84ace0ee7..a3dfb671b 100644 --- a/src/main/java/noppes/npcs/scripted/event/DialogEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/DialogEvent.java @@ -6,7 +6,7 @@ import noppes.npcs.api.event.IDialogEvent; import noppes.npcs.api.handler.data.IDialog; import noppes.npcs.constants.EnumScriptType; -public class DialogEvent extends CustomNPCsEvent implements IDialogEvent { +public abstract class DialogEvent extends CustomNPCsEvent implements IDialogEvent { public final IDialog dialog; public final IPlayer player; diff --git a/src/main/java/noppes/npcs/scripted/event/FactionEvent.java b/src/main/java/noppes/npcs/scripted/event/FactionEvent.java index 2741b669b..ed0aca4a1 100644 --- a/src/main/java/noppes/npcs/scripted/event/FactionEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/FactionEvent.java @@ -6,7 +6,7 @@ import noppes.npcs.api.event.IFactionEvent; import noppes.npcs.api.handler.data.IFaction; import noppes.npcs.constants.EnumScriptType; -public class FactionEvent extends CustomNPCsEvent implements IFactionEvent { +public abstract class FactionEvent extends CustomNPCsEvent implements IFactionEvent { public final IFaction faction; public final IPlayer player; diff --git a/src/main/java/noppes/npcs/scripted/event/ItemEvent.java b/src/main/java/noppes/npcs/scripted/event/ItemEvent.java index d94c0448b..23881e9ad 100644 --- a/src/main/java/noppes/npcs/scripted/event/ItemEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/ItemEvent.java @@ -7,7 +7,7 @@ import noppes.npcs.api.event.IItemEvent; import noppes.npcs.api.item.IItemCustom; import noppes.npcs.constants.EnumScriptType; -public class ItemEvent extends CustomNPCsEvent implements IItemEvent { +public abstract class ItemEvent extends CustomNPCsEvent implements IItemEvent { public final IItemCustom item; public ItemEvent(IItemCustom item) { diff --git a/src/main/java/noppes/npcs/scripted/event/NpcEvent.java b/src/main/java/noppes/npcs/scripted/event/NpcEvent.java index 98c51e6ba..1631f3d9d 100644 --- a/src/main/java/noppes/npcs/scripted/event/NpcEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/NpcEvent.java @@ -20,7 +20,7 @@ import noppes.npcs.scripted.NpcAPI; import java.util.ArrayList; -public class NpcEvent extends CustomNPCsEvent implements INpcEvent { +public abstract class NpcEvent extends CustomNPCsEvent implements INpcEvent { public final ICustomNpc npc; public NpcEvent(ICustomNpc npc) { diff --git a/src/main/java/noppes/npcs/scripted/event/PartyEvent.java b/src/main/java/noppes/npcs/scripted/event/PartyEvent.java index d1df03e8d..adaacc81f 100644 --- a/src/main/java/noppes/npcs/scripted/event/PartyEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/PartyEvent.java @@ -7,7 +7,7 @@ import noppes.npcs.api.handler.data.IParty; import noppes.npcs.api.handler.data.IQuest; import noppes.npcs.constants.EnumScriptType; -public class PartyEvent extends CustomNPCsEvent implements IPartyEvent { +public abstract class PartyEvent extends CustomNPCsEvent implements IPartyEvent { public final IQuest quest; public final IParty party; diff --git a/src/main/java/noppes/npcs/scripted/event/PlayerEvent.java b/src/main/java/noppes/npcs/scripted/event/PlayerEvent.java index e181668ac..0a57d95a5 100644 --- a/src/main/java/noppes/npcs/scripted/event/PlayerEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/PlayerEvent.java @@ -18,7 +18,7 @@ import noppes.npcs.api.item.IItemStack; import noppes.npcs.constants.EnumScriptType; import noppes.npcs.scripted.NpcAPI; -public class PlayerEvent extends CustomNPCsEvent implements IPlayerEvent { +public abstract class PlayerEvent extends CustomNPCsEvent implements IPlayerEvent { public final IPlayer player; public PlayerEvent(IPlayer player) { diff --git a/src/main/java/noppes/npcs/scripted/event/ProjectileEvent.java b/src/main/java/noppes/npcs/scripted/event/ProjectileEvent.java index e485396b5..060edfbe7 100644 --- a/src/main/java/noppes/npcs/scripted/event/ProjectileEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/ProjectileEvent.java @@ -4,8 +4,9 @@ import noppes.npcs.api.IBlock; import noppes.npcs.api.entity.IEntity; import noppes.npcs.api.entity.IProjectile; import noppes.npcs.api.event.IProjectileEvent; +import noppes.npcs.constants.EnumScriptType; -public class ProjectileEvent extends CustomNPCsEvent implements IProjectileEvent { +public abstract class ProjectileEvent extends CustomNPCsEvent implements IProjectileEvent { public IProjectile projectile; public IEntity source; @@ -29,6 +30,11 @@ public class ProjectileEvent extends CustomNPCsEvent implements IProjectileEvent public UpdateEvent(IProjectile projectile) { super(projectile); } + + @Override + public String getHookName() { + return EnumScriptType.PROJECTILE_TICK.function; + } } /** @@ -62,5 +68,10 @@ public class ProjectileEvent extends CustomNPCsEvent implements IProjectileEvent return (IBlock) target; return null; } + + @Override + public String getHookName() { + return EnumScriptType.PROJECTILE_IMPACT.function; + } } } diff --git a/src/main/java/noppes/npcs/scripted/event/QuestEvent.java b/src/main/java/noppes/npcs/scripted/event/QuestEvent.java index 264e6f8c8..c5261f951 100644 --- a/src/main/java/noppes/npcs/scripted/event/QuestEvent.java +++ b/src/main/java/noppes/npcs/scripted/event/QuestEvent.java @@ -7,7 +7,7 @@ import noppes.npcs.api.handler.data.IQuest; import noppes.npcs.api.item.IItemStack; import noppes.npcs.constants.EnumScriptType; -public class QuestEvent extends CustomNPCsEvent implements IQuestEvent { +public abstract class QuestEvent extends CustomNPCsEvent implements IQuestEvent { public final IQuest quest; public final IPlayer player; diff --git a/src/main/resources/assets/customnpcs/lang/en_US.lang b/src/main/resources/assets/customnpcs/lang/en_US.lang index 1ea355b10..1bb9d0f7e 100644 --- a/src/main/resources/assets/customnpcs/lang/en_US.lang +++ b/src/main/resources/assets/customnpcs/lang/en_US.lang @@ -1171,9 +1171,7 @@ animation.smoothing=Smoothing animation.linear=Linear animation.smooth=Smooth animation.loop=Loop -animation.tickType=Tick Type -animation.mcTicks=MC Ticks -animation.renderTicks=Render Ticks +animation.tickDuration=Tick Duration animation.whileStanding=While Standing animation.whileAttacking=While Attacking animation.whileMoving=While Moving