diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java
index 4cfeeb37..27c61abb 100644
--- a/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java
+++ b/src/main/java/com/minelittlepony/unicopia/client/render/entity/AirBalloonEntityRenderer.java
@@ -1,5 +1,6 @@
 package com.minelittlepony.unicopia.client.render.entity;
 
+import java.util.function.Function;
 import java.util.function.Predicate;
 
 import com.minelittlepony.unicopia.Unicopia;
@@ -19,8 +20,8 @@ import net.minecraft.util.math.Box;
 public class AirBalloonEntityRenderer extends MobEntityRenderer<AirBalloonEntity, AirBalloonEntityModel> {
     public AirBalloonEntityRenderer(EntityRendererFactory.Context context) {
         super(context, new AirBalloonEntityModel(AirBalloonEntityModel.getBasketModelData().createModel()), 0);
-        addFeature(new BalloonFeature("burner", new AirBalloonEntityModel(AirBalloonEntityModel.getBurnerModelData().createModel()), this, AirBalloonEntity::hasBurner));
-        addFeature(new BalloonFeature("canopy", new AirBalloonEntityModel(AirBalloonEntityModel.getCanopyModelData().createModel()), this, AirBalloonEntity::hasBalloon));
+        addFeature(new BalloonFeature(new AirBalloonEntityModel(AirBalloonEntityModel.getBurnerModelData().createModel()), this, AirBalloonEntity::hasBurner, e -> getComponentTexture("burner")));
+        addFeature(new BalloonFeature(new AirBalloonEntityModel(AirBalloonEntityModel.getCanopyModelData().createModel()), this, AirBalloonEntity::hasBalloon, e -> getComponentTexture("canopy/" + e.getDesign().asString())));
     }
 
     @Override
@@ -36,7 +37,7 @@ public class AirBalloonEntityRenderer extends MobEntityRenderer<AirBalloonEntity
 
     @Override
     public Identifier getTexture(AirBalloonEntity entity) {
-        return getComponentTexture(entity, "basket");
+        return getComponentTexture("basket/" + entity.getBasketType().asString());
     }
 
     @Override
@@ -44,29 +45,30 @@ public class AirBalloonEntityRenderer extends MobEntityRenderer<AirBalloonEntity
         return 0;
     }
 
-    private Identifier getComponentTexture(AirBalloonEntity entity, String componentName) {
+    private Identifier getComponentTexture(String componentName) {
         return Unicopia.id("textures/entity/air_balloon/" + componentName + ".png");
     }
 
     final class BalloonFeature extends FeatureRenderer<AirBalloonEntity, AirBalloonEntityModel> {
         private final AirBalloonEntityModel model;
         private final Predicate<AirBalloonEntity> visibilityTest;
-        private final String componentName;
+        private final Function<AirBalloonEntity, Identifier> textureFunc;
 
-        public BalloonFeature(String componentName, AirBalloonEntityModel model,
+        public BalloonFeature(AirBalloonEntityModel model,
                 FeatureRendererContext<AirBalloonEntity, AirBalloonEntityModel> context,
-                Predicate<AirBalloonEntity> visibilityTest) {
+                Predicate<AirBalloonEntity> visibilityTest,
+                Function<AirBalloonEntity, Identifier> textureFunc) {
             super(context);
-            this.componentName = componentName;
             this.model = model;
             this.visibilityTest = visibilityTest;
+            this.textureFunc = textureFunc;
         }
 
         @Override
         public void render(MatrixStack matrices, VertexConsumerProvider vertices, int light, AirBalloonEntity entity,
                 float limbAngle, float limbDistance, float tickDelta, float animationProgress, float yaw, float pitch) {
             if (visibilityTest.test(entity)) {
-                render(getModel(), model, getComponentTexture(entity, componentName), matrices, vertices, light, entity, limbAngle, limbDistance, 0, yaw, pitch, tickDelta, 1, 1, 1);
+                render(getModel(), model, textureFunc.apply(entity), matrices, vertices, light, entity, limbAngle, limbDistance, 0, yaw, pitch, tickDelta, 1, 1, 1);
             }
         }
     }
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java
index 5bb79eef..948431c6 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/AirBalloonEntity.java
@@ -5,36 +5,42 @@ import net.minecraft.entity.*;
 import net.minecraft.entity.data.*;
 import net.minecraft.entity.mob.FlyingEntity;
 import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.vehicle.BoatEntity;
 import net.minecraft.item.ItemStack;
 import net.minecraft.item.Items;
 import net.minecraft.nbt.NbtCompound;
 import net.minecraft.particle.ParticleTypes;
 import net.minecraft.sound.SoundEvents;
-import net.minecraft.text.Text;
 import net.minecraft.util.ActionResult;
 import net.minecraft.util.Hand;
+import net.minecraft.util.StringIdentifiable;
+import net.minecraft.util.function.ValueLists;
 import net.minecraft.util.math.*;
 import net.minecraft.util.math.random.Random;
 import net.minecraft.util.shape.VoxelShape;
 import net.minecraft.util.shape.VoxelShapes;
 import net.minecraft.world.World;
+import net.minecraft.world.event.GameEvent;
 
 import java.util.List;
+import java.util.Locale;
 import java.util.function.Consumer;
+import java.util.function.IntFunction;
 
 import com.minelittlepony.unicopia.entity.collision.EntityCollisions;
 import com.minelittlepony.unicopia.entity.collision.MultiBox;
 import com.minelittlepony.unicopia.entity.duck.EntityDuck;
-import com.minelittlepony.unicopia.item.UItems;
+import com.minelittlepony.unicopia.item.HotAirBalloonItem;
 import com.minelittlepony.unicopia.server.world.WeatherConditions;
 
 public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.ComplexCollidable, MultiBoundingBoxEntity {
-    private static final byte HAS_BALLOON = 1;
     private static final byte HAS_BURNER = 2;
     private static final byte BURNER_ACTIVE = 4;
-    private static final TrackedData<Integer> FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
+    private static final TrackedData<Integer> BURNER_FLAGS = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
     private static final TrackedData<Integer> BOOSTING = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
     private static final TrackedData<Integer> INFLATION = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
+    private static final TrackedData<Integer> BASKET_TYPE = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
+    private static final TrackedData<Integer> BALLOON_DESIGN = DataTracker.registerData(AirBalloonEntity.class, TrackedDataHandlerRegistry.INTEGER);
 
     private boolean prevBoosting;
     private int prevInflation;
@@ -43,23 +49,38 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
 
     public AirBalloonEntity(EntityType<? extends AirBalloonEntity> type, World world) {
         super(type, world);
+        intersectionChecked = true;
         setPersistent();
     }
 
     @Override
     protected void initDataTracker() {
         super.initDataTracker();
-        dataTracker.startTracking(FLAGS, 0);
+        dataTracker.startTracking(BURNER_FLAGS, 0);
         dataTracker.startTracking(BOOSTING, 0);
         dataTracker.startTracking(INFLATION, 0);
+        dataTracker.startTracking(BASKET_TYPE, 0);
+        dataTracker.startTracking(BALLOON_DESIGN, 0);
+    }
+
+    public BoatEntity.Type getBasketType() {
+        return BoatEntity.Type.getType(dataTracker.get(BASKET_TYPE));
+    }
+
+    public void setBasketType(BoatEntity.Type type) {
+        dataTracker.set(BASKET_TYPE, type.ordinal());
+    }
+
+    public BalloonDesign getDesign() {
+        return BalloonDesign.getType(dataTracker.get(BALLOON_DESIGN));
+    }
+
+    public void setDesign(BalloonDesign design) {
+        dataTracker.set(BALLOON_DESIGN, design.ordinal());
     }
 
     public boolean hasBalloon() {
-        return getFlag(HAS_BALLOON);
-    }
-
-    public void setHasBalloon(boolean hasBalloon) {
-        setFlag(HAS_BALLOON, hasBalloon);
+        return getDesign() != BalloonDesign.NONE;
     }
 
     public boolean hasBurner() {
@@ -87,7 +108,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
     }
 
     public boolean isBurnerActive() {
-        return getFlag((byte)(HAS_BURNER | BURNER_ACTIVE | HAS_BALLOON));
+        return hasBalloon() && getFlag((byte)(HAS_BURNER | BURNER_ACTIVE));
     }
 
     public void setBurnerActive(boolean burnerActive) {
@@ -103,12 +124,12 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
     }
 
     private boolean getFlag(byte flag) {
-        return (dataTracker.get(FLAGS).intValue() & flag) == flag;
+        return (dataTracker.get(BURNER_FLAGS).intValue() & flag) == flag;
     }
 
     private void setFlag(byte flag, boolean val) {
-        int v = dataTracker.get(FLAGS);
-        dataTracker.set(FLAGS, val ? (v | flag) : (v & ~flag));
+        int v = dataTracker.get(BURNER_FLAGS);
+        dataTracker.set(BURNER_FLAGS, val ? (v | flag) : (v & ~flag));
     }
 
     private boolean isAirworthy() {
@@ -212,20 +233,23 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
         prevBoosting = boosting;
         oldPosition = getPos();
 
-        for (Box box : getBoundingBoxes()) {
-            for (Entity e : getWorld().getOtherEntities(this, box.expand(0, 0.5, 0).stretch(getVelocity().multiply(-1)))) {
-                updatePassenger(e, box, e.getY() > getY() + 3);
-            }
-        }
-
         if (getFireTicks() > 0) {
             setFireTicks(1);
         }
 
+        updatePassengers();
         super.tick();
         setBoundingBox(MultiBox.of(getBoundingBox(), getBoundingBoxes()));
     }
 
+    private void updatePassengers() {
+        for (Box box : getBoundingBoxes()) {
+            for (Entity e : getWorld().getOtherEntities(this, box.expand(getVelocity().length()).expand(0, 0.5, 0))) {
+                updatePassenger(e, box, e.getY() > getY() + 3);
+            }
+        }
+    }
+
     private void updatePassenger(Entity e, Box box, boolean inBalloon) {
 
         double height = box.getYLength();
@@ -239,7 +263,14 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
             }
         }
 
-        e.setVelocity(e.getVelocity().multiply(0.1, 0.5, 0.1));
+        if (manualVelocity.length() > 0.01 || getVelocity().length() > 0.3) {
+            e.setVelocity(e.getVelocity().multiply(0.1, 0.5, 0.1));
+        }
+
+        if (getVelocity().y < 0) {
+            e.addVelocity(0, getVelocity().y, 0);
+            Living.updateVelocity(e);
+        }
 
         if (inBalloon && !e.isSneaky() && Math.abs(e.getVelocity().y) > 0.079) {
             e.setVelocity(e.getVelocity().multiply(1, e.getVelocity().y < 0 ? -0.9 : 1.2, 1).add(0, 0.8, 0));
@@ -257,8 +288,10 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
         if (getWorld().isClient) {
             if (e.distanceTraveled > ((EntityDuck)e).getNextStepSoundDistance()) {
                 e.distanceTraveled--;
-
                 e.playSound(inBalloon ? SoundEvents.BLOCK_WOOL_STEP : SoundEvents.BLOCK_BAMBOO_STEP, 0.5F, 1);
+                if (!e.isSneaky()) {
+                    getWorld().emitGameEvent(e, GameEvent.STEP, getBlockPos());
+                }
             }
         }
     }
@@ -276,6 +309,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
                     }
                     stack.damage(1, player, p -> p.sendEquipmentBreakStatus(hand == Hand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND));
                     playSound(SoundEvents.ITEM_FLINTANDSTEEL_USE, 1, 1);
+                    getWorld().emitGameEvent(this, GameEvent.ENTITY_INTERACT, getBlockPos());
                     return ActionResult.SUCCESS;
                 }
 
@@ -287,11 +321,11 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
                     }
                 } else if (stack.isEmpty() && isBurnerActive()) {
                     setBoostTicks(50);
+                    getWorld().emitGameEvent(this, GameEvent.ENTITY_INTERACT, getBlockPos());
                 }
             }
         }
 
-        player.sendMessage(Text.literal(hitPos + ""));
         return ActionResult.PASS;
     }
 
@@ -299,14 +333,13 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
     protected ActionResult interactMob(PlayerEntity player, Hand hand) {
         ItemStack stack = player.getStackInHand(hand);
 
-
-
-        if (stack.isOf(UItems.LARGE_BALLOON) && !hasBalloon()) {
+        if (stack.getItem() instanceof HotAirBalloonItem balloon && !hasBalloon()) {
             if (!player.getAbilities().creativeMode) {
                 stack.decrement(1);
             }
             playSound(SoundEvents.ITEM_ARMOR_EQUIP_LEATHER, 1, 1);
-            setHasBalloon(true);
+            getWorld().emitGameEvent(this, GameEvent.ENTITY_INTERACT, getBlockPos());
+            setDesign(HotAirBalloonItem.getDesign(getWorld(), stack));
             return ActionResult.SUCCESS;
         }
 
@@ -315,6 +348,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
                 stack.decrement(1);
             }
             playSound(SoundEvents.ENTITY_IRON_GOLEM_DAMAGE, 0.2F, 1);
+            getWorld().emitGameEvent(this, GameEvent.ENTITY_INTERACT, getBlockPos());
             setHasBurner(true);
             return ActionResult.SUCCESS;
         }
@@ -334,6 +368,7 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
 
     @Override
     public void pushAwayFrom(Entity entity) {
+
     }
 
     @Override
@@ -341,6 +376,12 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
 
     }
 
+    @Override
+    protected Entity.MoveEffect getMoveEffect() {
+        return Entity.MoveEffect.EVENTS;
+    }
+
+
     @Override
     public Box getVisibilityBoundingBox() {
         if (hasBalloon()) {
@@ -391,7 +432,8 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
     @Override
     public void readCustomDataFromNbt(NbtCompound compound) {
         super.readCustomDataFromNbt(compound);
-        setHasBalloon(compound.getBoolean("hasBalloon"));
+        setBasketType(BoatEntity.Type.getType(compound.getString("basketType")));
+        setDesign(BalloonDesign.getType(compound.getString("design")));
         setHasBurner(compound.getBoolean("hasBurner"));
         setBurnerActive(compound.getBoolean("burnerActive"));
         setBoostTicks(compound.getInt("boostTicks"));
@@ -402,12 +444,37 @@ public class AirBalloonEntity extends FlyingEntity implements EntityCollisions.C
     @Override
     public void writeCustomDataToNbt(NbtCompound compound) {
         super.writeCustomDataToNbt(compound);
-        compound.putBoolean("hasBalloon", hasBalloon());
+        compound.putString("design", getDesign().asString());
+        compound.putString("basket", getBasketType().asString());
         compound.putBoolean("hasBurner", hasBurner());
         compound.putBoolean("burnerActive", isBurnerActive());
         compound.putInt("boostTicks", getBoostTicks());
         compound.putInt("inflationAmount", getInflation());
     }
+
+    @SuppressWarnings("deprecation")
+    public enum BalloonDesign implements StringIdentifiable {
+        NONE,
+        LUNA;
+
+        public static final StringIdentifiable.Codec<BalloonDesign> CODEC = StringIdentifiable.createCodec(BalloonDesign::values);
+        private static final IntFunction<BalloonDesign> BY_ID = ValueLists.<BalloonDesign>createIdToValueFunction(Enum::ordinal, values(), ValueLists.OutOfBoundsHandling.ZERO);
+
+        private final String name = name().toLowerCase(Locale.ROOT);
+
+        @Override
+        public String asString() {
+            return name;
+        }
+
+        public static BalloonDesign getType(int type) {
+            return BY_ID.apply(type);
+        }
+
+        public static BalloonDesign getType(String name) {
+            return CODEC.byId(name, LUNA);
+        }
+    }
 }
 
 
diff --git a/src/main/java/com/minelittlepony/unicopia/entity/Living.java b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
index 8dce0045..5679cd66 100644
--- a/src/main/java/com/minelittlepony/unicopia/entity/Living.java
+++ b/src/main/java/com/minelittlepony/unicopia/entity/Living.java
@@ -77,6 +77,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
 
     @Nullable
     private Vec3d supportPositionOffset;
+    private int ticksOutsideVehicle;
 
     @Nullable
     private Caster<?> attacker;
@@ -181,6 +182,9 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
 
     public void setSupportingEntity(Entity supportingEntity) {
         this.supportingEntity = supportingEntity;
+        if (supportingEntity != null) {
+            ticksOutsideVehicle = 0;
+        }
     }
 
     public void setPositionOffset(@Nullable Vec3d positionOffset) {
@@ -191,19 +195,6 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
         setPositionOffset(supportingEntity == null ? null : entity.getPos().subtract(supportingEntity.getPos()));
     }
 
-    public static void checkGroundCollission(Entity entity, Box box) {
-        double height = box.getYLength();
-
-        if (height < 3 || entity.getBoundingBox().minY > box.minY + height / 2D) {
-            if (entity.getBoundingBox().minY < box.maxY) {
-                entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ());
-            }
-            if (entity.getBoundingBox().minY > box.maxY) {
-                entity.setPos(entity.getX(), box.maxY - 0.002, entity.getZ());
-            }
-        }
-    }
-
     public void updateRelativePosition(Box box) {
         if (supportingEntity == null || supportPositionOffset == null) {
             return;
@@ -251,11 +242,16 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
 
     public void updateSupportingEntity() {
         if (supportingEntity != null) {
-            Box ownBox = entity.getBoundingBox().expand(0.1);
+            Box ownBox = entity.getBoundingBox()
+                    .stretch(entity.getVelocity())
+                    .expand(0.1, 0.5, 0.1)
+                    .stretch(supportingEntity.getVelocity().multiply(-2));
 
-            MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream().filter(box -> {
-                return box.expand(0, 0.5, 0).intersects(ownBox);
-            }).findFirst().ifPresentOrElse(box -> {
+            MultiBoundingBoxEntity.getBoundingBoxes(supportingEntity).stream()
+            .filter(box -> box.stretch(supportingEntity.getVelocity()).expand(0, 0.5, 0).intersects(ownBox))
+            .findFirst()
+            .ifPresentOrElse(box -> {
+                ticksOutsideVehicle = 0;
                 if (supportPositionOffset == null) {
                     updatePositionOffset();
                 } else {
@@ -265,8 +261,14 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
                 entity.verticalCollision = true;
                 entity.groundCollision = true;
             }, () -> {
-                supportingEntity = null;
-                supportPositionOffset = null;
+                // Rubberband passengers to try and prevent players falling out when the velocity changes suddenly
+                if (ticksOutsideVehicle++ > 30) {
+                    supportingEntity = null;
+                    supportPositionOffset = null;
+                    Unicopia.LOGGER.info("Entity left vehicle");
+                } else {
+                    supportPositionOffset = supportPositionOffset.multiply(0.25, 1, 0.25);
+                }
             });
         }
 
@@ -320,7 +322,10 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
         }
 
         updateDragonBreath();
-        updatePositionOffset();
+
+        if (ticksOutsideVehicle == 0) {
+            updatePositionOffset();
+        }
     }
 
     public void updateAttributeModifier(UUID id, EntityAttribute attribute, float desiredValue, Float2ObjectFunction<EntityAttributeModifier> modifierSupplier, boolean permanent) {
diff --git a/src/main/java/com/minelittlepony/unicopia/item/BasketItem.java b/src/main/java/com/minelittlepony/unicopia/item/BasketItem.java
index 6161eb58..35ee94f6 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/BasketItem.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/BasketItem.java
@@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.util.Dispensable;
 import net.minecraft.block.DispenserBlock;
 import net.minecraft.entity.Entity;
 import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.entity.vehicle.BoatEntity;
 import net.minecraft.item.BoatItem;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
@@ -31,8 +32,11 @@ public class BasketItem extends Item implements Dispensable {
     private static final Predicate<Entity> RIDERS = EntityPredicates.EXCEPT_SPECTATOR.and(Entity::canHit);
     private static final double REACH = 5;
 
-    public BasketItem(Item.Settings settings) {
+    private final BoatEntity.Type type;
+
+    public BasketItem(BoatEntity.Type type, Item.Settings settings) {
         super(settings);
+        this.type = type;
         DispenserBlock.registerBehavior(this, createDispenserBehaviour());
     }
 
@@ -71,6 +75,7 @@ public class BasketItem extends Item implements Dispensable {
         entity.updatePositionAndAngles(x, y, z, 0, 0);
         entity.setHeadYaw(yaw);
         entity.setBodyYaw(yaw);
+        entity.setBasketType(type);
         if (!world.isSpaceEmpty(entity, entity.getBoundingBox())) {
             return TypedActionResult.fail(stack);
         }
diff --git a/src/main/java/com/minelittlepony/unicopia/item/HotAirBalloonItem.java b/src/main/java/com/minelittlepony/unicopia/item/HotAirBalloonItem.java
new file mode 100644
index 00000000..dca94801
--- /dev/null
+++ b/src/main/java/com/minelittlepony/unicopia/item/HotAirBalloonItem.java
@@ -0,0 +1,24 @@
+package com.minelittlepony.unicopia.item;
+
+import com.minelittlepony.unicopia.entity.AirBalloonEntity;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+public class HotAirBalloonItem extends Item {
+
+    public HotAirBalloonItem(Settings settings) {
+        super(settings);
+    }
+
+    public static AirBalloonEntity.BalloonDesign getDesign(World world, ItemStack stack) {
+        String design;
+        if (stack.hasNbt() && !(design = stack.getNbt().getString("design")).isEmpty()) {
+            return AirBalloonEntity.BalloonDesign.getType(design);
+        }
+
+        int ordinal = 1 + world.getRandom().nextInt(AirBalloonEntity.BalloonDesign.values().length - 1);
+        return AirBalloonEntity.BalloonDesign.getType(ordinal);
+    }
+}
diff --git a/src/main/java/com/minelittlepony/unicopia/item/UItems.java b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
index 8e6aa552..5e6a382d 100644
--- a/src/main/java/com/minelittlepony/unicopia/item/UItems.java
+++ b/src/main/java/com/minelittlepony/unicopia/item/UItems.java
@@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.item.group.ItemGroupRegistry;
 import com.minelittlepony.unicopia.item.group.UItemGroups;
 import com.minelittlepony.unicopia.item.toxin.UFoodComponents;
 
+import net.minecraft.entity.vehicle.BoatEntity;
 import net.minecraft.item.*;
 import net.minecraft.item.Item.Settings;
 import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
@@ -118,8 +119,8 @@ public interface UItems {
     Item BUTTERFLY = register("butterfly", new Item(new Item.Settings().food(UFoodComponents.INSECTS)), ItemGroups.FOOD_AND_DRINK);
 
     Item SPELLBOOK = register("spellbook", new SpellbookItem(new Item.Settings().maxCount(1).rarity(Rarity.UNCOMMON)), ItemGroups.TOOLS);
-    Item BASKET = register("basket", new BasketItem(new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
-    Item LARGE_BALLOON = register("large_balloon", new Item(new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
+    Item OAK_BASKET = register("oak_basket", new BasketItem(BoatEntity.Type.OAK, new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
+    Item GIANT_BALLOON = register("giant_balloon", new HotAirBalloonItem(new Item.Settings().maxCount(1)), ItemGroups.FUNCTIONAL);
 
     AmuletItem PEGASUS_AMULET = register("pegasus_amulet", new PegasusAmuletItem(new FabricItemSettings()
             .maxCount(1)
diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json
index e0767889..f96d69d2 100644
--- a/src/main/resources/assets/unicopia/lang/en_us.json
+++ b/src/main/resources/assets/unicopia/lang/en_us.json
@@ -23,6 +23,9 @@
   "item.unicopia.friendship_bracelet.issuer": "Signed by %s",
   "item.unicopia.friendship_bracelet.glowing": "Glowing",
 
+  "item.unicopia.oak_basket": "Oak Basket",
+  "item.unicopia.giant_balloon": "Giant Balloon",
+
   "item.unicopia.spellbook": "Spellbook",
   "emi.category.unicopia.spellbook": "Spellbook",
 
diff --git a/src/main/resources/assets/unicopia/models/item/giant_balloon.json b/src/main/resources/assets/unicopia/models/item/giant_balloon.json
new file mode 100644
index 00000000..4df680ac
--- /dev/null
+++ b/src/main/resources/assets/unicopia/models/item/giant_balloon.json
@@ -0,0 +1,6 @@
+{
+    "parent": "item/generated",
+    "textures": {
+        "layer0": "unicopia:item/giant_balloon"
+    }
+}
diff --git a/src/main/resources/assets/unicopia/models/item/oak_basket.json b/src/main/resources/assets/unicopia/models/item/oak_basket.json
new file mode 100644
index 00000000..6302a186
--- /dev/null
+++ b/src/main/resources/assets/unicopia/models/item/oak_basket.json
@@ -0,0 +1,6 @@
+{
+    "parent": "item/generated",
+    "textures": {
+        "layer0": "unicopia:item/oak_basket"
+    }
+}
diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon/basket.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon/basket/oak.png
similarity index 100%
rename from src/main/resources/assets/unicopia/textures/entity/air_balloon/basket.png
rename to src/main/resources/assets/unicopia/textures/entity/air_balloon/basket/oak.png
diff --git a/src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy.png b/src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy/luna.png
similarity index 100%
rename from src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy.png
rename to src/main/resources/assets/unicopia/textures/entity/air_balloon/canopy/luna.png
diff --git a/src/main/resources/assets/unicopia/textures/item/giant_balloon.png b/src/main/resources/assets/unicopia/textures/item/giant_balloon.png
new file mode 100644
index 00000000..31c7d9ea
Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/giant_balloon.png differ
diff --git a/src/main/resources/assets/unicopia/textures/item/oak_basket.png b/src/main/resources/assets/unicopia/textures/item/oak_basket.png
new file mode 100644
index 00000000..0dd46d28
Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/item/oak_basket.png differ