@@ -0,0 +1,2204 @@
+package com.elmakers.mine.bukkit.action;%0A%0Aimport com.elmakers.mine.bukkit.api.action.CastContext;%0Aimport com.elmakers.mine.bukkit.api.effect.EffectPlayer;%0Aimport com.elmakers.mine.bukkit.api.spell.SpellResult;%0Aimport org.bukkit.configuration.ConfigurationSection;%0Aimport org.bukkit.entity.Entity;%0A%0Aimport java.util.Collection;%0A%0Apublic abstract class BaseProjectileAction extends CompoundAction %7B%0A    private long lifetime;%0A%0A    protected Entity tracking;%0A    private long expiration;%0A%0A    @Override%0A    public void prepare(CastContext context, ConfigurationSection parameters) %7B%0A        super.prepare(context, parameters);%0A        lifetime = parameters.getLong(%22lifetime%22, 10000);%0A    %7D%0A%0A    @Override%0A    public void reset(CastContext context) %7B%0A        super.reset(context);%0A        expiration = System.currentTimeMillis() + lifetime;%0A    %7D%0A%0A    @Override%0A    public SpellResult step(CastContext context)%0A    %7B%0A        if (tracking == null) %7B%0A            return SpellResult.CAST;%0A        %7D%0A%0A        if (System.currentTimeMillis() %3E expiration) %7B%0A            tracking.remove();%0A            tracking = null;%0A            return SpellResult.NO_TARGET;%0A        %7D%0A%0A        if (!tracking.isValid()) %7B%0A            createActionContext(context, tracking, tracking.getLocation(), null, tracking.getLocation());%0A            actionContext.playEffects(%22hit%22);%0A            tracking = null;%0A            return startActions();%0A        %7D%0A%0A        return SpellResult.PENDING;%0A    %7D%0A%0A    protected void playProjectileEffects(CastContext context) %7B%0A        Collection%3CEffectPlayer%3E projectileEffects = context.getEffects(%22projectile%22);%0A        for (EffectPlayer effectPlayer : projectileEffects) %7B%0A            effectPlayer.start(tracking.getLocation(), tracking, null, null);%0A        %7D%0A    %7D%0A%0A    protected SpellResult checkTracking(CastContext context) %7B%0A        if (tracking == null) %7B%0A            return SpellResult.FAIL;%0A        %7D%0A        if (!hasActions()) %7B%0A            // Don't bother tracking if we're not doing anything on hit%0A            if (!context.hasEffects(%22hit%22)) %7B%0A                tracking = null;%0A            %7D%0A            return SpellResult.CAST;%0A        %7D%0A%0A        return SpellResult.NO_ACTION;%0A    %7D%0A%7D%0A

Add a base action for "projectile" type actions. (Used for ThrowBlock, Firework, etc.. not actually for Projectile or CustomProjectile yet)