Skip to content

Commit f04580d

Browse files
committed
Add equippable component to item meta
1 parent cd6e75d commit f04580d

File tree

6 files changed

+364
-7
lines changed

6 files changed

+364
-7
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.laytonsmith.abstraction;
2+
3+
import com.laytonsmith.abstraction.enums.MCEntityType;
4+
import com.laytonsmith.abstraction.enums.MCEquipmentSlot;
5+
6+
import java.util.Collection;
7+
8+
public interface MCEquippableComponent extends AbstractionObject {
9+
MCEquipmentSlot getSlot();
10+
void setSlot(MCEquipmentSlot slot);
11+
Collection<MCEntityType> getAllowedEntities();
12+
void setAllowedEntities(Collection<MCEntityType> types);
13+
String getCameraOverlay();
14+
void setCameraOverlay(String overlay);
15+
String getAssetId();
16+
void setAssetId(String assetId);
17+
String getEquipSound();
18+
void setEquipSound(String sound);
19+
boolean isDispensable();
20+
void setDispensable(boolean dispensable);
21+
boolean isEquipOnInteract();
22+
void setEquipOnInteract(boolean equipOnInteract);
23+
boolean isSwappable();
24+
void setSwappable(boolean swappable);
25+
boolean isDamageOnHurt();
26+
void setDamageOnHurt(boolean damagedOnHurt);
27+
}

src/main/java/com/laytonsmith/abstraction/MCItemMeta.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,4 +220,10 @@ public interface MCItemMeta extends AbstractionObject {
220220

221221
void setUseCooldown(MCCooldownComponent component);
222222

223+
boolean hasEquippable();
224+
225+
MCEquippableComponent getEquippable();
226+
227+
void setEquippable(MCEquippableComponent component);
228+
223229
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
package com.laytonsmith.abstraction.bukkit;
2+
3+
import com.laytonsmith.abstraction.MCEquippableComponent;
4+
import com.laytonsmith.abstraction.enums.MCEntityType;
5+
import com.laytonsmith.abstraction.enums.MCEquipmentSlot;
6+
import com.laytonsmith.abstraction.enums.bukkit.BukkitMCEntityType;
7+
import com.laytonsmith.abstraction.enums.bukkit.BukkitMCEquipmentSlot;
8+
import org.bukkit.NamespacedKey;
9+
import org.bukkit.Registry;
10+
import org.bukkit.Sound;
11+
import org.bukkit.entity.EntityType;
12+
import org.bukkit.inventory.meta.components.EquippableComponent;
13+
14+
import java.util.ArrayList;
15+
import java.util.Collection;
16+
17+
public class BukkitMCEquippableComponent implements MCEquippableComponent {
18+
19+
private final EquippableComponent equippableComponent;
20+
21+
public BukkitMCEquippableComponent(EquippableComponent foodComponent) {
22+
this.equippableComponent = foodComponent;
23+
}
24+
25+
@Override
26+
public MCEquipmentSlot getSlot() {
27+
return BukkitMCEquipmentSlot.getConvertor().getAbstractedEnum(this.equippableComponent.getSlot());
28+
}
29+
30+
@Override
31+
public void setSlot(MCEquipmentSlot slot) {
32+
this.equippableComponent.setSlot(BukkitMCEquipmentSlot.getConvertor().getConcreteEnum(slot));
33+
}
34+
35+
@Override
36+
public Collection<MCEntityType> getAllowedEntities() {
37+
Collection<EntityType> allowedEntities = this.equippableComponent.getAllowedEntities();
38+
if(allowedEntities == null) {
39+
return null;
40+
}
41+
Collection<MCEntityType> ret = new ArrayList<>();
42+
for(EntityType type : allowedEntities) {
43+
ret.add(BukkitMCEntityType.valueOfConcrete(type));
44+
}
45+
return ret;
46+
}
47+
48+
@Override
49+
public void setAllowedEntities(Collection<MCEntityType> types) {
50+
if(types == null) {
51+
this.equippableComponent.setAllowedEntities((EntityType) null);
52+
} else {
53+
Collection<EntityType> entityTypes = new ArrayList<>();
54+
for(MCEntityType type : types) {
55+
entityTypes.add((EntityType) type.getConcrete());
56+
}
57+
this.equippableComponent.setAllowedEntities(entityTypes);
58+
}
59+
}
60+
61+
@Override
62+
public String getCameraOverlay() {
63+
if(this.equippableComponent.getCameraOverlay() == null) {
64+
return null;
65+
}
66+
return this.equippableComponent.getCameraOverlay().toString();
67+
}
68+
69+
@Override
70+
public void setCameraOverlay(String overlay) {
71+
if(overlay == null) {
72+
this.equippableComponent.setCameraOverlay(null);
73+
} else {
74+
this.equippableComponent.setCameraOverlay(NamespacedKey.fromString(overlay));
75+
}
76+
}
77+
78+
@Override
79+
public String getAssetId() {
80+
if(this.equippableComponent.getModel() == null) {
81+
return null;
82+
}
83+
return this.equippableComponent.getModel().toString();
84+
}
85+
86+
@Override
87+
public void setAssetId(String assetId) {
88+
if(assetId == null) {
89+
this.equippableComponent.setModel(null);
90+
} else {
91+
this.equippableComponent.setModel(NamespacedKey.fromString(assetId));
92+
}
93+
}
94+
95+
@Override
96+
public String getEquipSound() {
97+
Sound sound = this.equippableComponent.getEquipSound();
98+
// API may say this cannot be null, but it can be at runtime.
99+
if(sound == null) {
100+
return null;
101+
}
102+
try {
103+
return sound.getKey().toString();
104+
} catch(NullPointerException | IllegalStateException ex) {
105+
// Probably a new sound event definition instead of a key, so we have no choice but to return null for now.
106+
// The ISException is probably a server bug. NPE catch is just in case getKey() nullability is changed.
107+
// Registry.SOUNDS.getKey(sound) is cleaner, as it can return null, but it's Paper only.
108+
return null;
109+
}
110+
}
111+
112+
@Override
113+
public void setEquipSound(String sound) {
114+
if(sound == null) {
115+
this.equippableComponent.setEquipSound(null);
116+
} else {
117+
NamespacedKey key = NamespacedKey.fromString(sound);
118+
if(key != null) {
119+
this.equippableComponent.setEquipSound(Registry.SOUNDS.get(key));
120+
}
121+
}
122+
}
123+
124+
@Override
125+
public boolean isDispensable() {
126+
return this.equippableComponent.isDispensable();
127+
}
128+
129+
@Override
130+
public void setDispensable(boolean dispensable) {
131+
this.equippableComponent.setDispensable(dispensable);
132+
}
133+
134+
@Override
135+
public boolean isEquipOnInteract() {
136+
return this.equippableComponent.isEquipOnInteract();
137+
}
138+
139+
@Override
140+
public void setEquipOnInteract(boolean equipOnInteract) {
141+
this.equippableComponent.setEquipOnInteract(equipOnInteract);
142+
}
143+
144+
@Override
145+
public boolean isSwappable() {
146+
return this.equippableComponent.isSwappable();
147+
}
148+
149+
@Override
150+
public void setSwappable(boolean swappable) {
151+
this.equippableComponent.setSwappable(swappable);
152+
}
153+
154+
@Override
155+
public boolean isDamageOnHurt() {
156+
return this.equippableComponent.isDamageOnHurt();
157+
}
158+
159+
@Override
160+
public void setDamageOnHurt(boolean damagedOnHurt) {
161+
this.equippableComponent.setDamageOnHurt(damagedOnHurt);
162+
}
163+
164+
@Override
165+
public Object getHandle() {
166+
return equippableComponent;
167+
}
168+
169+
@Override
170+
public String toString() {
171+
return equippableComponent.toString();
172+
}
173+
174+
@Override
175+
public boolean equals(Object o) {
176+
return o instanceof BukkitMCEquippableComponent && equippableComponent.equals(((BukkitMCEquippableComponent) o).equippableComponent);
177+
}
178+
179+
@Override
180+
public int hashCode() {
181+
return equippableComponent.hashCode();
182+
}
183+
}

src/main/java/com/laytonsmith/abstraction/bukkit/BukkitMCItemMeta.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.laytonsmith.abstraction.AbstractionObject;
88
import com.laytonsmith.abstraction.MCAttributeModifier;
99
import com.laytonsmith.abstraction.MCCooldownComponent;
10+
import com.laytonsmith.abstraction.MCEquippableComponent;
1011
import com.laytonsmith.abstraction.MCFoodComponent;
1112
import com.laytonsmith.abstraction.MCItemMeta;
1213
import com.laytonsmith.abstraction.MCItemStack;
@@ -49,6 +50,7 @@
4950
import org.bukkit.inventory.meta.Damageable;
5051
import org.bukkit.inventory.meta.ItemMeta;
5152
import org.bukkit.inventory.meta.Repairable;
53+
import org.bukkit.inventory.meta.components.EquippableComponent;
5254
import org.bukkit.inventory.meta.components.FoodComponent;
5355
import org.bukkit.inventory.meta.components.JukeboxPlayableComponent;
5456
import org.bukkit.inventory.meta.components.UseCooldownComponent;
@@ -490,4 +492,19 @@ public void setUseCooldown(MCCooldownComponent component) {
490492
im.setUseCooldown((UseCooldownComponent) component.getHandle());
491493
}
492494

495+
@Override
496+
public boolean hasEquippable() {
497+
return im.hasEquippable();
498+
}
499+
500+
@Override
501+
public MCEquippableComponent getEquippable() {
502+
return new BukkitMCEquippableComponent(im.getEquippable());
503+
}
504+
505+
@Override
506+
public void setEquippable(MCEquippableComponent component) {
507+
im.setEquippable((EquippableComponent) component.getHandle());
508+
}
509+
493510
}

src/main/java/com/laytonsmith/core/ObjectGenerator.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.laytonsmith.abstraction.MCCreatureSpawner;
1717
import com.laytonsmith.abstraction.MCCrossbowMeta;
1818
import com.laytonsmith.abstraction.MCEnchantmentStorageMeta;
19+
import com.laytonsmith.abstraction.MCEquippableComponent;
1920
import com.laytonsmith.abstraction.MCFireworkBuilder;
2021
import com.laytonsmith.abstraction.MCFireworkEffect;
2122
import com.laytonsmith.abstraction.MCFireworkEffectMeta;
@@ -108,6 +109,7 @@
108109
import com.laytonsmith.core.natives.interfaces.Mixed;
109110

110111
import java.util.ArrayList;
112+
import java.util.Collection;
111113
import java.util.HashMap;
112114
import java.util.List;
113115
import java.util.Locale;
@@ -566,6 +568,39 @@ public Construct itemMeta(MCItemStack is, Target t) {
566568
if(meta.hasTooltipStyle()) {
567569
ma.set("tooltipstyle", new CString(meta.getTooltipStyle(), t), t);
568570
}
571+
if(meta.hasEquippable()) {
572+
MCEquippableComponent equippableComponent = meta.getEquippable();
573+
CArray equippable = CArray.GetAssociativeArray(t);
574+
MCEquipmentSlot slot = equippableComponent.getSlot();
575+
equippable.set("slot", new CString(slot.name(), t), t);
576+
String asset = equippableComponent.getAssetId();
577+
if(asset != null) {
578+
equippable.set("asset", new CString(asset, t), t);
579+
}
580+
String cameraOverlay = equippableComponent.getCameraOverlay();
581+
if(cameraOverlay != null) {
582+
equippable.set("overlay", new CString(cameraOverlay, t), t);
583+
}
584+
Collection<MCEntityType> allowedEntities = equippableComponent.getAllowedEntities();
585+
if(allowedEntities != null) {
586+
CArray entities = new CArray(t);
587+
for(MCEntityType type : allowedEntities) {
588+
entities.push(new CString(type.name(), t), t);
589+
}
590+
equippable.set("entities", entities, t);
591+
}
592+
String sound = equippableComponent.getEquipSound();
593+
if(sound != null) {
594+
equippable.set("sound", new CString(sound, t), t);
595+
}
596+
equippable.set("dispensable", CBoolean.get(equippableComponent.isDispensable()), t);
597+
equippable.set("swappable", CBoolean.get(equippableComponent.isSwappable()), t);
598+
equippable.set("damageable", CBoolean.get(equippableComponent.isDamageOnHurt()), t);
599+
if(Static.getServer().getMinecraftVersion().gte(MCVersion.MC1_21_5)) {
600+
equippable.set("interactable", CBoolean.get(equippableComponent.isEquipOnInteract()), t);
601+
}
602+
ma.set("equippable", equippable, t);
603+
}
569604
}
570605

571606
} else { // versions before 1.20.6
@@ -1190,6 +1225,78 @@ public MCItemMeta itemMeta(Mixed c, MCMaterial mat, Target t) throws ConfigRunti
11901225
meta.setTooltipStyle(tooltipstyle.val());
11911226
}
11921227
}
1228+
if(ma.containsKey("equippable")) {
1229+
Mixed equippableMixed = ma.get("equippable", t);
1230+
if(equippableMixed instanceof CNull) {
1231+
// not yet supported
1232+
} else if(equippableMixed.isInstanceOf(CArray.TYPE)) {
1233+
CArray equippableArray = (CArray) equippableMixed;
1234+
if(!equippableArray.isAssociative()) {
1235+
throw new CREFormatException("Equippable array must be associative.", t);
1236+
}
1237+
MCEquippableComponent equippable = meta.getEquippable();
1238+
try {
1239+
String slotName = equippableArray.get("slot", t).val().toUpperCase();
1240+
equippable.setSlot(MCEquipmentSlot.valueOf(slotName));
1241+
} catch (IllegalArgumentException ex) {
1242+
throw new CREFormatException("Not a valid equipment slot: "
1243+
+ equippableArray.get("slot", t).val(), t);
1244+
}
1245+
if(equippableArray.containsKey("entities")) {
1246+
Mixed entitiesMixed = equippableArray.get("entities", t);
1247+
if(entitiesMixed instanceof CNull) {
1248+
// ignored
1249+
} else if(entitiesMixed.isInstanceOf(CArray.TYPE)) {
1250+
CArray entitiesArray = (CArray) entitiesMixed;
1251+
if(entitiesArray.isAssociative()) {
1252+
throw new CREFormatException("Allowed entities array must not be associative.", t);
1253+
}
1254+
Collection<MCEntityType> allowedEntities = new ArrayList<>();
1255+
for(Mixed type : entitiesArray) {
1256+
allowedEntities.add(MCEntityType.valueOf(type.val()));
1257+
}
1258+
equippable.setAllowedEntities(allowedEntities);
1259+
}
1260+
}
1261+
if(equippableArray.containsKey("overlay")) {
1262+
Mixed cameraOverlayMixed = equippableArray.get("overlay", t);
1263+
if(!(cameraOverlayMixed instanceof CNull)) {
1264+
equippable.setCameraOverlay(cameraOverlayMixed.val());
1265+
}
1266+
}
1267+
if(equippableArray.containsKey("damageable")) {
1268+
equippable.setDamageOnHurt(ArgumentValidation.getBooleanObject(
1269+
equippableArray.get("damageable", t), t));
1270+
}
1271+
if(equippableArray.containsKey("dispensable")) {
1272+
equippable.setDispensable(ArgumentValidation.getBooleanObject(
1273+
equippableArray.get("dispensable", t), t));
1274+
}
1275+
if(equippableArray.containsKey("asset")) {
1276+
Mixed assetMixed = equippableArray.get("asset", t);
1277+
if(!(assetMixed instanceof CNull)) {
1278+
equippable.setAssetId(assetMixed.val());
1279+
}
1280+
}
1281+
if(equippableArray.containsKey("sound")) {
1282+
Mixed soundMixed = equippableArray.get("sound", t);
1283+
if(!(soundMixed instanceof CNull)) {
1284+
equippable.setEquipSound(soundMixed.val());
1285+
}
1286+
}
1287+
if(equippableArray.containsKey("swappable")) {
1288+
equippable.setSwappable(ArgumentValidation.getBooleanObject(equippableArray.get("swappable", t), t));
1289+
}
1290+
if(Static.getServer().getMinecraftVersion().gte(MCVersion.MC1_21_5)) {
1291+
if(equippableArray.containsKey("interactable")) {
1292+
equippable.setEquipOnInteract(ArgumentValidation.getBooleanObject(equippableArray.get("interactable", t), t));
1293+
}
1294+
}
1295+
meta.setEquippable(equippable);
1296+
} else {
1297+
throw new CREFormatException("Expected an array for item equippable component.", t);
1298+
}
1299+
}
11931300
}
11941301
if(ma.containsKey("damage")) {
11951302
Mixed damage = ma.get("damage", t);

0 commit comments

Comments
 (0)