Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add off hand support for Armor stands #520

Merged
merged 10 commits into from
Jul 8, 2017
174 changes: 101 additions & 73 deletions src/main/java/net/glowstone/entity/objects/GlowArmorStand.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package net.glowstone.entity.objects;

import com.flowpowered.network.Message;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import net.glowstone.EventFactory;
import net.glowstone.GlowWorld;
import net.glowstone.entity.GlowLivingEntity;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.entity.meta.MetadataIndex;
import net.glowstone.entity.meta.MetadataIndex.ArmorStandFlags;
import net.glowstone.entity.meta.MetadataIndex.StatusFlags;
import net.glowstone.inventory.ClothType;
import net.glowstone.inventory.GlowEntityEquipment;
import net.glowstone.net.message.play.entity.DestroyEntitiesMessage;
import net.glowstone.net.message.play.entity.EntityEquipmentMessage;
import net.glowstone.net.message.play.entity.EntityMetadataMessage;
Expand All @@ -20,23 +26,26 @@
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.*;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Projectile;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.PlayerArmorStandManipulateEvent;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scoreboard.Criterias;
import org.bukkit.scoreboard.Objective;
import org.bukkit.util.EulerAngle;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class GlowArmorStand extends GlowLivingEntity implements ArmorStand {

private static final EulerAngle[] defaultPose = new EulerAngle[6];
Expand All @@ -54,8 +63,7 @@ public class GlowArmorStand extends GlowLivingEntity implements ArmorStand {
}


private final ItemStack[] equipment = new ItemStack[5];
private final boolean[] changedEquip = new boolean[5];
private final GlowEntityEquipment equipment;
private final EulerAngle[] pose = new EulerAngle[6];

private boolean isMarker;
Expand All @@ -70,18 +78,15 @@ public class GlowArmorStand extends GlowLivingEntity implements ArmorStand {
public GlowArmorStand(Location location) {
super(location, 2);

equipment = new GlowEntityEquipment(this);

System.arraycopy(defaultPose, 0, pose, 0, 6);
for (int i = 0; i < 5; i++) {
changedEquip[i] = false;
}
this.getEquipmentMonitor().resetChanges();
}

@Override
public void reset() {
super.reset();
for (int i = 0; i < 5; i++) {
changedEquip[i] = false;
}
if (needsKill) needsKill = false;
}

Expand Down Expand Up @@ -151,8 +156,8 @@ private void setHealth(double health, boolean drop) {
private void kill(boolean dropArmorStand) {
active = false;
((GlowWorld) location.getWorld()).showParticle(location.clone().add(0, 1.317, 0), Effect.TILE_DUST, Material.WOOD.getId(), 0, 0.125f, 0.494f, 0.125f, 0.1f, 10, 10);
for (ItemStack stack : equipment) {
if (stack == null || stack.getType() == Material.AIR) continue;
for (ItemStack stack : equipment.getArmorContents()) {
if (InventoryUtil.isEmpty(stack)) continue;
getWorld().dropItemNaturally(location, stack);
}
if (dropArmorStand) {
Expand All @@ -165,8 +170,9 @@ public boolean entityInteract(GlowPlayer player, InteractEntityMessage msg) {
if (player.getGameMode() == GameMode.SPECTATOR || isMarker) return false;
if (msg.getAction() == Action.INTERACT_AT.ordinal()) {
if (InventoryUtil.isEmpty(player.getItemInHand())) {
int slot = getEditSlot(msg.getTargetY());
PlayerArmorStandManipulateEvent event = new PlayerArmorStandManipulateEvent(player, this, InventoryUtil.itemOrEmpty(null), InventoryUtil.itemOrEmpty(equipment[slot]), EquipmentSlot.values()[slot]);
EquipmentSlot slot = getEditSlot(msg.getTargetY());

PlayerArmorStandManipulateEvent event = new PlayerArmorStandManipulateEvent(player, this, InventoryUtil.itemOrEmpty(null), InventoryUtil.itemOrEmpty(equipment.getItem(slot)), slot);
EventFactory.callEvent(event);

if (event.isCancelled()) {
Expand All @@ -177,18 +183,17 @@ public boolean entityInteract(GlowPlayer player, InteractEntityMessage msg) {
return false;
}

ItemStack stack = equipment[slot];
ItemStack stack = equipment.getItem(slot);
player.setItemInHand(stack);
equipment[slot] = InventoryUtil.createEmptyStack();
changedEquip[slot] = true;
equipment.setItem(slot, InventoryUtil.createEmptyStack());
return true;
} else {
int slot = getEquipType(player.getItemInHand().getType());
if (slot == 0 && !hasArms) {
EquipmentSlot slot = getEquipType(player.getItemInHand().getType());
if ((slot == EquipmentSlot.HAND || slot == EquipmentSlot.OFF_HAND) && !hasArms) {
return false;
}

PlayerArmorStandManipulateEvent event = new PlayerArmorStandManipulateEvent(player, this, player.getItemInHand(), InventoryUtil.itemOrEmpty(equipment[slot]), EquipmentSlot.values()[slot]);
PlayerArmorStandManipulateEvent event = new PlayerArmorStandManipulateEvent(player, this, player.getItemInHand(), InventoryUtil.itemOrEmpty(equipment.getItem(slot)), slot);
EventFactory.callEvent(event);

if (event.isCancelled()) {
Expand All @@ -208,21 +213,44 @@ public boolean entityInteract(GlowPlayer player, InteractEntityMessage msg) {
if (stack.getAmount() > 1) {
return false;
}
back = equipment[slot];
back = equipment.getItem(slot);
}

if (!InventoryUtil.isEmpty(back)) {
player.setItemInHand(back);
}
equipment[slot] = stack;
changedEquip[slot] = true;
equipment.setItem(slot, stack);
player.playSound(location, getEquipSound(stack.getType()), SoundCategory.NEUTRAL, 1, 1);
return true;
}
}
return false;
}

private int getEquipType(Material mat) {
private Sound getEquipSound(Material mat) {
if (mat == Material.ELYTRA) {
return Sound.ITEM_ARMOR_EQUIP_ELYTRA;
}
if (ClothType.LEATHER.matches(mat)) {
return Sound.ITEM_ARMOR_EQUIP_LEATHER;
}
if (ClothType.CHAINMAIL.matches(mat)) {
return Sound.ITEM_ARMOR_EQUIP_CHAIN;
}
if (ClothType.IRON.matches(mat)) {
return Sound.ITEM_ARMOR_EQUIP_IRON;
}
if (ClothType.GOLD.matches(mat)) {
return Sound.ITEM_ARMOR_EQUIP_GOLD;
}
if (ClothType.DIAMOND.matches(mat)) {
return Sound.ITEM_ARMOR_EQUIP_DIAMOND;
}

return Sound.ITEM_ARMOR_EQUIP_GENERIC;
}

private EquipmentSlot getEquipType(Material mat) {
switch (mat) {
case IRON_HELMET:
case LEATHER_HELMET:
Expand All @@ -231,47 +259,50 @@ private int getEquipType(Material mat) {
case DIAMOND_HELMET:
case PUMPKIN:
case SKULL_ITEM:
return 4;
return EquipmentSlot.HEAD;
case IRON_CHESTPLATE:
case GOLD_CHESTPLATE:
case LEATHER_CHESTPLATE:
case CHAINMAIL_CHESTPLATE:
case DIAMOND_CHESTPLATE:
return 3;
case ELYTRA:
return EquipmentSlot.CHEST;
case IRON_LEGGINGS:
case GOLD_LEGGINGS:
case LEATHER_LEGGINGS:
case CHAINMAIL_LEGGINGS:
case DIAMOND_LEGGINGS:
return 2;
return EquipmentSlot.LEGS;
case IRON_BOOTS:
case GOLD_BOOTS:
case LEATHER_BOOTS:
case CHAINMAIL_BOOTS:
case DIAMOND_BOOTS:
return 1;
return EquipmentSlot.FEET;
case SHIELD:
return EquipmentSlot.OFF_HAND;
default:
return 0;
return EquipmentSlot.HAND;
}
}

private int getEditSlot(float height) {
int slot = 0;
private EquipmentSlot getEditSlot(float height) {
if (isSmall) height *= 2;
if (height >= 0.1 && height < 0.1 + (isSmall ? 0.8 : 0.45) && !isEmpty(1)) {
slot = 1;
} else if (height >= 0.9 + (isSmall ? 0.3 : 0) && height < 0.9 + (isSmall ? 1 : 0.7) && !isEmpty(3)) {
slot = 3;
} else if (height >= 0.4 && height < 0.4 + (isSmall ? 1 : 0.8) && !isEmpty(2)) {
slot = 2;
} else if (height >= 1.6 && !isEmpty(4)) {
slot = 4;

if (height >= 0.1 && height < 0.1 + (isSmall ? 0.8 : 0.45) && !isEmpty(EquipmentSlot.FEET)) {
return EquipmentSlot.FEET;
} else if (height >= 0.9 + (isSmall ? 0.3 : 0) && height < 0.9 + (isSmall ? 1 : 0.7) && !isEmpty(EquipmentSlot.CHEST)) {
return EquipmentSlot.CHEST;
} else if (height >= 0.4 && height < 0.4 + (isSmall ? 1 : 0.8) && !isEmpty(EquipmentSlot.LEGS)) {
return EquipmentSlot.LEGS;
} else if (height >= 1.6 && !isEmpty(EquipmentSlot.HEAD)) {
return EquipmentSlot.HEAD;
}
return slot;
return EquipmentSlot.HAND;
}

private boolean isEmpty(int slot) {
return InventoryUtil.isEmpty(equipment[slot]);
private boolean isEmpty(EquipmentSlot slot) {
return InventoryUtil.isEmpty(equipment.getItem(slot));
}

@Override
Expand Down Expand Up @@ -299,27 +330,24 @@ public List<Message> createSpawnMessage() {
int pitch = Position.getIntPitch(location);

return Arrays.asList(
new SpawnObjectMessage(id, UUID.randomUUID(), 78, x, y, z, pitch, yaw), // TODO: once UUID is documented, actually use the appropriate ID here
new EntityMetadataMessage(id, metadata.getEntryList()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.HELD_ITEM, getItemInHand()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.BOOTS_SLOT, getBoots()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.LEGGINGS_SLOT, getLeggings()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.CHESTPLATE_SLOT, getChestplate()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.HELMET_SLOT, getHelmet())
new SpawnObjectMessage(id, UUID.randomUUID(), 78, x, y, z, pitch, yaw), // TODO: once UUID is documented, actually use the appropriate ID here
new EntityMetadataMessage(id, metadata.getEntryList()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.HELD_ITEM, getItemInHand()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.OFF_HAND, equipment.getItemInOffHand()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.BOOTS_SLOT, getBoots()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.LEGGINGS_SLOT, getLeggings()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.CHESTPLATE_SLOT, getChestplate()),
new EntityEquipmentMessage(id, EntityEquipmentMessage.HELMET_SLOT, getHelmet())
);
}

@Override
public List<Message> createUpdateMessage() {
List<Message> messages = super.createUpdateMessage();
for (int i = 0; i < 5; i++) {
if (changedEquip[i]) {
messages.add(new EntityEquipmentMessage(id, i, equipment[i]));
}
}
if (needsKill) {
messages.add(new DestroyEntitiesMessage(Arrays.asList(id)));
messages.add(new DestroyEntitiesMessage(Collections.singletonList(id)));
}

return messages;
}

Expand All @@ -335,57 +363,52 @@ public Location getOrigin() {

@Override
public ItemStack getItemInHand() {
return equipment[0];
return equipment.getItemInHand();
}

@Override
public void setItemInHand(ItemStack item) {
equipment[0] = item;
changedEquip[0] = true;
equipment.setItemInHand(item);
}

@Override
public ItemStack getBoots() {
return equipment[1];
return equipment.getBoots();
}

@Override
public void setBoots(ItemStack item) {
equipment[1] = item;
changedEquip[1] = true;
equipment.setBoots(item);
}

@Override
public ItemStack getLeggings() {
return equipment[2];
return equipment.getLeggings();
}

@Override
public void setLeggings(ItemStack item) {
equipment[2] = item;
changedEquip[2] = true;
equipment.setLeggings(item);
}

@Override
public ItemStack getChestplate() {
return equipment[3];
return equipment.getChestplate();
}

@Override
public void setChestplate(ItemStack item) {
equipment[3] = item;
changedEquip[3] = true;
equipment.setChestplate(item);
}

@Override
public ItemStack getHelmet() {
return equipment[4];
return equipment.getHelmet();
}

@Override
public void setHelmet(ItemStack item) {
equipment[4] = item;
changedEquip[4] = true;
equipment.setHelmet(item);
}

@Override
Expand Down Expand Up @@ -574,4 +597,9 @@ public int getArrowsStuck() {
public void setArrowsStuck(int i) {

}

@Override
public EntityEquipment getEquipment() {
return this.equipment;
}
}
Loading