This is the quick guide on how to set up the very basics, if you would like to have more functionality and customization then check the advanced section.
Reference the API
Of course, with any API you need to reference the library, unfortunately, I couldn't get maven to work so you will need to download directly the resource here from spigot and add the jar to your referenced libraries. Once you have done so, now you need to add the dependency to your plugin.yml file like so:
depend: [UIFramework]
Quick rundown
In this guide, I will demonstrate how to create a custom lightsaber that will have the ability to throw it and return to its owner.
You will need to register your custom items, abilities, and crafting recipes within your onEnable method for your items to work. There are three registries you will need to know of, the first is the ability registry which you need to use to register abilities. Next, you need to register your actual custom items with the item registry. Finally, the recipe registry and your custom recipes can only be registered after your items since it needs the item to finish the recipe.
Good to know: UIFramework will automatically save and sync recipes so it does not matter if you register a recipe every time you initialize as it will be handled internally. Even if the user changes a recipe then reregistering it will not overwrite the user's recipe. However, if the user deletes one of your recipes and then you register it again it will reappear, to prevent this you should try to only register your recipe once.
publicclassMainextendsJavaPlugin {publicvoidonEnable() {registerAbilities();registerItems();registerRecipes();//init stuff }privatevoidregisterAbilities() {//ability to throw lightsaberAbilityType.registerAbility("saber_throw",SaberThrow.class); }privatevoidregisterItems() {//the actual lightsaber itemItemType.registerItem("green_lightsaber",GreenLightsaber.class); }privatevoidregisterRecipes() {//random recipe for the lightsaberShapedRecipe recipe =newShapedRecipe(new NamespacedKey(this,"recipe1"),ItemType.getItemType("green_lightsaber").getBuilder().getItem());recipe.shape(" a"," a ","b ");//its best to use RecipeChoice hererecipe.setIngredient('a',new RecipeChoice.ExactChoice(newItemStack(Material.EMERALD)));recipe.setIngredient('b',new RecipeChoice.ExactChoice(newItemStack(Material.STICK))); }}
Good to know: UIFramework contains an ItemBuilder class to handle items so attached to every ItemType instance is a builder that holds the item. To retrieve the item just call the ItemType.getBuilder() to get the builder and then ItemBuilder.getItem() to retrieve the ItemStack like shown in the shaped recipe object in the above snippet.
Create ability
As you might have noticed in the above code that we are registering an AbilityType with the class SaberThrow. The registry takes the name identifier of our ability and a class object that extends UIFrameworks Ability class. Let me show you an example of an ability class that would be acceptable for the registry object.
importcom.jewishbanana.uiframework.items.Ability;publicclassSaberThrowextendsAbility {//this is an override method, there are multiple overrides you can use. This one will activate when a player interacts//you want to return true if the ability activates, otherwise just return falsepublicbooleaninteracted(PlayerInteractEvent event,GenericItem base) {//the use method is a super method that will return true if the entity is not on cooldown and can use the ability, if returned true then it will automatically put the entity on cooldown againif (use(event.getPlayer())) {//the player was not on cooldown and since use() returned true it automatically placed them on cooldown//we will send them the message "Used Saber Throw" and return true since the ability activatedevent.getPlayer().sendMessage("Used "+this.getType().getName());returntrue; }//the player was on cooldown since use() returned false so we return false for the use of the abilityreturnfalse; }//this is going to return to the super class the cooldown ticks the entities who are passed through use() will be put on if successfully returned true//for the sake of the simplicity I use this but I don't recommend using it like this, a better way will be shown in the advanced sectionpublicintgetCooldownTicks() {return5*20; }}
This ability is finished for now and can be directly registered as an ability with the code we used above in the onEnable method:
As you have noticed we are overriding a method from the Ability class inside our code public boolean interacted(PlayerInteractEvent event, GenericItem base) This override will trigger if the player interacted in a certain way (More about that later). Here is a list of default methods from the Ability class that can be overridden in your custom abilities class:
//this is the internal use method, this can be overridden for better functionality if wantedpublicbooleanuse(Entity entity) {if (entity instanceof Player && ((!opCooldowns &&entity.isOp()) || (!immuneCooldowns &&UIFUtils.isPlayerImmune((Player) entity))))returntrue;if (type.isEntityOnCooldown(entity.getUniqueId())) {if (entity instanceof Player) ((Player) entity).sendMessage(UIFUtils.convertString(UIFDataUtils.getConfigString("messages.abilityCooldown").replace("%cooldown%",UIFDataUtils.getDecimalFormatted((double) (type.getEntityCooldown(entity.getUniqueId())) /20.0)).replace("%ability%",type.getName())));returnfalse; } else {type.putEntityOnCooldown(entity.getUniqueId(),getCooldownTicks());returntrue; }}//this is the interacted method and can trigger from PlayerInteractEventspublicbooleaninteracted(PlayerInteractEvent event,GenericItem base) {returnfalse;}//this method will trigger if the item was used to hit an entitypublicbooleanhitEntity(EntityDamageByEntityEvent event,GenericItem base) {returnfalse;}//this will trigger if the item was held/worn by an entity and got hit by another entitypublicbooleanwasHit(EntityDamageByEntityEvent event,GenericItem base) {returnfalse;}//this will trigger if the item was thrown (Only throwable items can trigger this)publicbooleanprojectileThrown(ProjectileLaunchEvent event,GenericItem base) {returnfalse;}//this will trigger if the item was thrown and hit something (Only throwable items can trigger this including arrows)publicbooleanprojectileHit(ProjectileHitEvent event,GenericItem base) {returnfalse;}//this will trigger if the item is a bow and was shot (arrows that are custom items will trigger a projectileHit inside their own class too)publicbooleanshotBow(EntityShootBowEvent event,GenericItem base) {returnfalse;}//this method should be overridden if you spawn other entities like armor stands inside your ability, it will be run if the server is turned off to prevent random floating armor stands and suchpublicvoidclean() {}
Create item
Now we can create our custom item and register it very similarly to how we register abilities, the reason why we register our custom items is to maintain even further control on actions and attributes of items. For the registry of the custom item it takes a string for the name identifier of the item (This will be the name used for the UIF give command in game), and it takes a class object that extends UIFrameworks GenericItem class. Here is an example of our green lightsaber item class:
importcom.jewishbanana.uiframework.utils.ItemBuilder;importcom.jewishbanana.uiframework.items.GenericItem;publicclassGreenLightsaberextendsGenericItem {//DO NOT add arguments to the constructor this needs to strictly have only an ItemStack argument passed which will be the custom item on initialization when it is detectedpublicGreenLightsaber(ItemStack item) { super(item); }//this is the UIF item builder that is passed for the items creation, this has a few builder blocks you can explore but for now we keep it simple//you can also just pass directly ItemBuilder.create(ItemStack) if you want to create an ItemStack first and pass it through//the assembleLore(id) will just automatically set up the lore with UIF default item lore template including info on abilities, stats, and enchants I HIGHLY RECOMMEND USING THIS TEMPLATE @OverridepublicItemBuildercreateItem() {returnItemBuilder.create(Material.GOLDEN_SWORD).registerName(UIFUtils.convertString("(hex:#1fe615)Green Lightsaber")).assembleLore(id).setCustomModelData(7000).build(); }}
Good to know: As you can see above in the createItem section the creator has a couple of good things to be aware of. When I use the method registerName, this will set the items name, but I use another method inside UIFUtils.convertString(). This method is part of a utilities class in UIF and will take in any string and convert the chat colors including hex colors input as "(hex:#000000)" whats also really nice about this method is that if the server does not support hex colors (running craftbukkit). Then it will automatically convert hex colors in the string to the closest vanilla color.
This item is finished for now and can be directly registered as an item with the code we used above in the onEnable method:
Now even though I showed a very basic way of setting up items and abilities in the onEnable abilities, I highly recommend you initialize them in a slightly different way.
importcom.jewishbanana.uiframework.items.Ability;importcom.jewishbanana.uiframework.items.Ability.Action;importcom.jewishbanana.uiframework.items.AbilityType;importcom.jewishbanana.uiframework.items.ItemType;privatevoidregisterAbilities() {//ability to throw lightsaber, the registerAbility method returns the AbilityType object itselfAbilityType type =AbilityType.registerAbility("saber_throw",SaberThrow.class);//we set the name for our custom ability, this is used by the assembleLore feature for the item loretype.setName(UIFUtils.convertString("&e[Saber Throw]"));//we set the description for our ability, this is also used by the assembleLore featuretype.setDescription(UIFUtils.convertString("&8- &7The wielder throws the saber dealing lots of damage. The saber will return."));}privatevoidregisterItems() {//the actual lightsaber item, the registerItem method returns the ItemType object itself to further modifyItemType type =ItemType.registerItem("green_lightsaber",GreenLightsaber.class);//we set the damage, attackspeed, and durability of the item. These values must be set upon registration everytime as they are not saved anywhere in UIFtype.setDamage(11.0);type.setAttackSpeed(1.8);type.setDurability(100.0);//we set the item types lore, this will also be added inside the assembleLore methodtype.setLore(Arrays.asList(UIFUtils.convertString("&eGlowing green blade that only true masters can wield.")));//now we add our custom ability and we bind it to the right-click action, this means that when this item is held and right clicked it will run our interacted method code inside our ability class//we can get the ability type like shown and then we create a new instance of our ability class which will be unique to this item type, item types can be grabbed statically similarly to how we do here for our saber throw abilitytype.addAbility(Action.RIGHT_CLICK,AbilityType.getAbilityType("saber_throw").createNewInstance());//finally, we must reassemble the item builder so that it updates the items properties like thistype.getBuilder().assembleLore(type).build();}
There we go, we fully finished our custom item! To get your item you can simply run this code:
Or in-game, the player can type the command /ui give green_lightsaber since all registered items will automatically be added to the UIF give command. That's it for the basic item and ability setup, pretty quick isn't it?
Further improvement
Now this next part is just to demonstrate the saber throw ability I earlier described, here I will show the code to make a simple ability that will throw the custom item and return it to its holder (This has some other calls and will not work if you copy paste it is just reference code):
importcom.jewishbanana.uiframework.items.GenericItem;publicclassSaberThrowextendsAbilityAttributes {privateArmorStand stand;privatePair<UUID,Pair<ItemStack,EquipmentSlot>> returnItem;privateGenericItem base;publicbooleaninteracted(PlayerInteractEvent event,GenericItem base) {if (use(event.getPlayer())) {Player entity =event.getPlayer(); stand =entity.getWorld().spawn(entity.getLocation().add(0,100,0),ArmorStand.class, temp -> {Utils.lockArmorStand(temp,true,false,true);temp.getEquipment().setItemInMainHand(base.getItem());temp.setRightArmPose(newEulerAngle(0,0,0)); });stand.teleport(entity);stand.teleport(stand.getLocation().add(0,entity.getHeight()/2-0.3,0));this.base= base; returnItem =Pair.of(entity.getUniqueId(),Pair.of(base.getItem().clone(),Utils.getEquipmentSlot(entity.getEquipment(),base.getItem())));entity.getInventory().remove(base.getItem());finaldouble travelSpeed =0.8;Vector dir =entity.getEyeLocation().getDirection().normalize().multiply(travelSpeed).add(newVector(0,0.02,0));double[] ticks = {range,100};newRepeatingTask(0,1) { @Overridepublicvoidrun() {if (stand ==null||stand.isDead() || entity ==null|| ticks[1] <=0) {clean();cancel();return; }if (ticks[0] <=0) { ticks[1]--;stand.teleport(stand.getLocation().add(Utils.getVectorTowards(stand.getLocation().add(0,1,0),entity.getLocation().add(0,entity.getHeight()/2,0)).multiply(travelSpeed)));if (stand.getLocation().distanceSquared(entity.getLocation()) <=0.8) ticks[1] =0; } else { ticks[0] -= travelSpeed;stand.teleport(stand.getLocation().add(dir));if (!stand.getLocation().add(0,0.7,0).getBlock().isPassable()) ticks[0] =0; }stand.setRotation(stand.getLocation().getYaw()+40,0);for (Entity e :stand.getWorld().getNearbyEntities(stand.getLocation().add(0,1,0),0.7,0.7,0.7, i ->!i.equals(entity) && i instanceof LivingEntity &&!(i instanceof Tameable &&DataUtils.isEqualsNoNull(entity, ((Tameable) i).getOwner())))) { ticks[0] =0;Utils.damageEntity((LivingEntity) e, damage,"deaths.saberThrow",false, entity);e.setVelocity(e.getVelocity().add(Utils.getVectorTowards(stand.getLocation().add(0,1,0),e.getLocation().add(0,e.getHeight()/2,0)).setY(0.1).multiply(0.1*knockbackMultiplier))); } } };returntrue; }returnfalse; }//clean method to remove the armor stand and return the itempublicvoidclean() {if (returnItem !=null) {Player p =Bukkit.getPlayer(returnItem.getFirst());if (p !=null&&p.isOnline()) {if (p.getInventory().firstEmpty() ==-1)base.setItem(p.getWorld().dropItemNaturally(p.getLocation(),returnItem.getSecond().getFirst()).getItemStack());elseif (returnItem.getSecond().getSecond() !=null&&p.getEquipment().getItem(returnItem.getSecond().getSecond()).getType() ==Material.AIR)p.getInventory().setItem(returnItem.getSecond().getSecond(),base.setItem(returnItem.getSecond().getFirst()).getItem());elsep.getInventory().addItem(base.setItem(returnItem.getSecond().getFirst()).getItem()); } elseif (stand !=null)base.setItem(stand.getWorld().dropItemNaturally(stand.getLocation(),returnItem.getSecond().getFirst()).getItemStack()); returnItem =null; }if (stand !=null)stand.remove(); }}
Now if we go into the game here is our newly created item and ability in action: