1
0
Fork 0
mirror of https://github.com/haselkern/Minecraft-ArmorStand.git synced 2025-05-17 15:05:33 +00:00

Support 1.20.5 and 1.21 + more

* Add support for versions 1.20.5+ and 1.21 (Fixes #49).
* Add `elytra` as an option for chestplates.
* Reimplement the code generation of the armor stand, to make it easy to support future versions.
This commit is contained in:
Lenny Lord 2024-08-07 16:22:26 +05:30 committed by thelennylord
parent 46864f706c
commit ea0195b36b
2 changed files with 447 additions and 336 deletions

View file

@ -32,7 +32,9 @@
<div class="padding underline"> <div class="padding underline">
<select id="mcversion"> <select id="mcversion">
<option value="1.16">Minecraft 1.16 and above</option> <option value="1.21">Minecraft 1.21 and above</option>
<option value="1.20.5">Minecraft 1.20.5 &amp; 1.20.6</option>
<option value="1.16">Minecraft 1.16 to 1.20.4</option>
<option value="1.14">Minecraft 1.14 &amp; 1.15</option> <option value="1.14">Minecraft 1.14 &amp; 1.15</option>
<option value="1.13">Minecraft 1.13</option> <option value="1.13">Minecraft 1.13</option>
<option value="1.11">Minecraft 1.11 &amp; 1.12</option> <option value="1.11">Minecraft 1.11 &amp; 1.12</option>
@ -116,48 +118,49 @@
<input type="text" class="equipment" name="equipShoes" placeholder="Boots" list="list-shoes"> <input type="text" class="equipment" name="equipShoes" placeholder="Boots" list="list-shoes">
<datalist id="list-shoes"> <datalist id="list-shoes">
<option value="chainmail_boots"> <option value="netherite_boots">
<option value="diamond_boots"> <option value="diamond_boots">
<option value="golden_boots"> <option value="golden_boots">
<option value="netherite_boots">
<option value="iron_boots"> <option value="iron_boots">
<option value="chainmail_boots">
<option value="leather_boots"> <option value="leather_boots">
</datalist> </datalist>
<div class="colorfield" id="shoecolor"></div> <div class="colorfield" id="boots_color"></div>
<input type="text" class="equipment" name="equipLeggings" placeholder="Leggings" list="list-leggings"> <input type="text" class="equipment" name="equipLeggings" placeholder="Leggings" list="list-leggings">
<datalist id="list-leggings"> <datalist id="list-leggings">
<option value="chainmail_leggings"> <option value="netherite_leggings">
<option value="diamond_leggings"> <option value="diamond_leggings">
<option value="golden_leggings"> <option value="golden_leggings">
<option value="netherite_leggings">
<option value="iron_leggings"> <option value="iron_leggings">
<option value="chainmail_leggings">
<option value="leather_leggings"> <option value="leather_leggings">
</datalist> </datalist>
<div class="colorfield" id="leggingscolor"></div> <div class="colorfield" id="leggings_color"></div>
<input type="text" class="equipment" name="equipChestplate" placeholder="Chestplate" list="list-chestplate"> <input type="text" class="equipment" name="equipChestplate" placeholder="Chestplate" list="list-chestplate">
<datalist id="list-chestplate"> <datalist id="list-chestplate">
<option value="chainmail_chestplate"> <option value="netherite_chestplate">
<option value="diamond_chestplate"> <option value="diamond_chestplate">
<option value="golden_chestplate"> <option value="golden_chestplate">
<option value="netherite_chestplate">
<option value="iron_chestplate"> <option value="iron_chestplate">
<option value="chainmail_chestplate">
<option value="leather_chestplate"> <option value="leather_chestplate">
<option value="elytra">
</datalist> </datalist>
<div class="colorfield" id="chestplatecolor"></div> <div class="colorfield" id="chestplate_color"></div>
<input type="text" class="equipment" name="equipHelmet" placeholder="Helmet" list="list-helmet"> <input type="text" class="equipment" name="equipHelmet" placeholder="Helmet" list="list-helmet">
<datalist id="list-helmet"> <datalist id="list-helmet">
<option value="turtle_helmet"> <option value="netherite_helmet">
<option value="chainmail_helmet">
<option value="diamond_helmet"> <option value="diamond_helmet">
<option value="golden_helmet"> <option value="golden_helmet">
<option value="netherite_helmet">
<option value="iron_helmet"> <option value="iron_helmet">
<option value="chainmail_helmet">
<option value="leather_helmet"> <option value="leather_helmet">
<option value="turtle_helmet">
</datalist> </datalist>
<div class="colorfield" id="helmetcolor"></div> <div class="colorfield" id="helmet_color"></div>
<br>Helmet specifies <br>Helmet specifies
<select id="equipCustomHeadMode"> <select id="equipCustomHeadMode">

View file

@ -355,7 +355,9 @@ const MC_VERSION = Object.freeze({
v1_11: 2, v1_11: 2,
v1_13: 3, v1_13: 3,
v1_14: 4, v1_14: 4,
v1_16: 5 v1_16: 5,
v1_20_5: 6,
v1_21: 7
}); });
function getMcVersion() { function getMcVersion() {
@ -387,10 +389,10 @@ function handleInput(){
equipHelmet = getInput("equipHelmet"); equipHelmet = getInput("equipHelmet");
equipCustomHeadMode = $("#equipCustomHeadMode").val(); equipCustomHeadMode = $("#equipCustomHeadMode").val();
equipColorShoes = $("#shoecolor").css("background-color"); equipColorShoes = $("#boots_color").css("background-color");
equipColorLeggings = $("#leggingscolor").css("background-color"); equipColorLeggings = $("#leggings_color").css("background-color");
equipColorChestplate = $("#chestplatecolor").css("background-color"); equipColorChestplate = $("#chestplate_color").css("background-color");
equipColorHelmet = $("#helmetcolor").css("background-color"); equipColorHelmet = $("#helmet_color").css("background-color");
customName = getInput("customname"); customName = getInput("customname");
showCustomName = getCheckBoxInput("showcustomname"); showCustomName = getCheckBoxInput("showcustomname");
@ -441,34 +443,63 @@ function updateUI(){
if(useEquipment){ if(useEquipment){
$("#customequipment").slideDown(); $("#customequipment").slideDown();
// Hide left hand item input for minecraft 1.8 // Hide left hand item input for minecraft 1.8
if(mcVersion == "1.8"){ if(mcVersion < MC_VERSION.v1_9) {
$("#equipHandLeft").hide(); $("#equipHandLeft").hide();
} } else {
else{
$("#equipHandLeft").show(); $("#equipHandLeft").show();
} }
let chestplate = chestplateList;
let helmet = helmetList;
let boots = bootsList;
let leggings = leggingsList;
// Hide elytra for versions <1.9
if (mcVersion < MC_VERSION.v1_9) {
chestplate = chestplate.filter("[value!=elytra]");
}
// Hide turtle_helmet for versions <1.13
if (mcVersion < MC_VERSION.v1_13) {
helmet = helmet.filter("[value!=turtle_helmet]");
}
// Hide netherite armor for versions < 1.16
if (mcVersion < MC_VERSION.v1_16) {
helmet = helmet.filter("[value!=netherite_helmet]");
chestplate = chestplate.filter("[value!=netherite_chestplate]");
leggings = leggings.filter("[value!=netherite_leggings]");
boots = boots.filter("[value!=netherite_boots]");
}
$("#list-helmet").empty().append(helmet);
$("#list-chestplate").empty().append(chestplate);
$("#list-leggings").empty().append(leggings);
$("#list-shoes").empty().append(boots);
} }
else else
$("#customequipment").slideUp(); $("#customequipment").slideUp();
//Different colorinputs for armorparts //Different colorinputs for armorparts
if(isLeatherArmor(equipShoes)) if(isLeatherArmor(equipShoes))
$("#shoecolor").slideDown(); $("#boots_color").slideDown();
else else
$("#shoecolor").slideUp(); $("#boots_color").slideUp();
if(isLeatherArmor(equipLeggings)) if(isLeatherArmor(equipLeggings))
$("#leggingscolor").slideDown(); $("#leggings_color").slideDown();
else else
$("#leggingscolor").slideUp(); $("#leggings_color").slideUp();
if(isLeatherArmor(equipChestplate)) if(isLeatherArmor(equipChestplate))
$("#chestplatecolor").slideDown(); $("#chestplate_color").slideDown();
else else
$("#chestplatecolor").slideUp(); $("#chestplate_color").slideUp();
if(isLeatherArmor(equipHelmet)) if(isLeatherArmor(equipHelmet))
$("#helmetcolor").slideDown(); $("#helmet_color").slideDown();
else else
$("#helmetcolor").slideUp(); $("#helmet_color").slideUp();
// Link to minecraft-heads.com // Link to minecraft-heads.com
if(equipCustomHeadMode == "givecode"){ if(equipCustomHeadMode == "givecode"){
@ -541,321 +572,345 @@ function updateUI(){
mSkull.visible = equipHelmet != ""; mSkull.visible = equipHelmet != "";
} }
function generateCode(){ function generateCode() {
var code = "/summon armor_stand ~ ~ ~ {" //in 1.13, positions are no longer center-corrected. Adding .5 makes it centered. However for players it is already center-corrected const tags = {
Invisible: invisible || null,
if (!give) { Invulnerable: invulnerable || null,
// Old entity name PersistenceRequired: persistencerequired || null,
if (mcVersion <= MC_VERSION.v1_9) { NoBasePlate: noBasePlate || null,
code = "/summon ArmorStand ~ ~ ~ {"; NoGravity: noGravity || null,
} else if (mcVersion == MC_VERSION.v1_11) { ShowArms: showArms || null,
code = "/summon armor_stand ~ ~ ~ {"; Small: small || null,
} else { Marker: marker || null,
centercorrected ? code = "/summon armor_stand ~ ~-0.5 ~ {" : code = "/summon armor_stand ~ ~ ~ {"
}
} else {
if(mcVersion <= MC_VERSION.v1_11){
code = "/give @p minecraft:armor_stand 1 0 {EntityTag:{";
} else {
code = "/give @p armor_stand{EntityTag:{"
}
}
var tags = []; Rotation: (rotation != 0) ? [ new NBTFloat(rotation) ] : null,
//CheckBoxes CustomNameVisible: showCustomName || null,
if(invisible) CustomName: (customName) ? generateCustomName() : null,
tags.push("Invisible:1b");
if(invulnerable)
tags.push("Invulnerable:1b");
if(persistencerequired)
tags.push("PersistenceRequired:1b");
if(noBasePlate)
tags.push("NoBasePlate:1b");
if(noGravity)
tags.push("NoGravity:1b");
if(showArms)
tags.push("ShowArms:1b");
if(small)
tags.push("Small:1b");
if(marker)
tags.push("Marker:1b");
//Sliders DisabledSlots: (useDisabledSlots) ? calculateDisabledSlotsFlag() : null,
if(rotation != 0) Pose: generatePose(),
tags.push("Rotation:["+rotation+"f]"); };
// Equipment // Equipment
if(useEquipment){ if (useEquipment) {
// Old 1.8 Equipment format
if (mcVersion == MC_VERSION.v1_8) { if (mcVersion == MC_VERSION.v1_8) {
var armor = []; // Old 1.8 Equipment format
// Equipment: [ RightHand, Boots, Leggings, Chestplate, Helmet ]
armor.push(getHandRightItem()); const armorItems = generateArmorItems();
armor.push(getShoesItem()); const handItems = generateHandItems();
armor.push(getLeggingsItem()); tags.Equipment = armorItems.splice(0, 0, handItems[0]);
armor.push(getChestplateItem());
armor.push(getHeadItem());
tags.push("Equipment:["+armor.join(",")+"]");
}
// New 1.9+ Equipment format
else{
var armor = [];
armor.push(getShoesItem());
armor.push(getLeggingsItem());
armor.push(getChestplateItem());
armor.push(getHeadItem());
tags.push("ArmorItems:["+armor.join(",")+"]");
var hands = [];
hands.push(getHandRightItem());
hands.push(getHandLeftItem());
tags.push("HandItems:["+hands.join(",")+"]");
}
// Hide netherite armour for lower versions
if (mcVersion <= MC_VERSION.v1_11) {
$("#list-helmet").empty().append(helmetList.filter("[value!=netherite_helmet]").filter("[value!=turtle_helmet]"));
$("#list-chestplate").empty().append(chestplateList.filter("[value!=netherite_chestplate]").filter("[value!=turtle_chestplate]"));
$("#list-leggings").empty().append(leggingsList.filter("[value!=netherite_leggings]").filter("[value!=turtle_leggings]"));
$("#list-shoes").empty().append(bootsList.filter("[value!=netherite_boots]").filter("[value!=turtle_boots]"));
} else if (mcVersion <= MC_VERSION.v1_14) {
$("#list-helmet").empty().append(helmetList.filter("[value!=netherite_helmet]"));
$("#list-chestplate").empty().append(chestplateList.filter("[value!=netherite_chestplate]"));
$("#list-leggings").empty().append(leggingsList.filter("[value!=netherite_leggings]"));
$("#list-shoes").empty().append(bootsList.filter("[value!=netherite_boots]"));
} else { } else {
$("#list-helmet").empty().append(helmetList); // New 1.9+ Equipment format
$("#list-chestplate").empty().append(chestplateList); if (equipShoes != "" || equipLeggings != "" || equipChestplate != "" || equipHelmet != "") {
$("#list-leggings").empty().append(leggingsList); tags.ArmorItems = generateArmorItems();
$("#list-shoes").empty().append(bootsList); }
if (equipHandRight != "" || equipHandLeft != "") {
tags.HandItems = generateHandItems();
}
} }
} }
// Custom name // Scoreboard tags
if(customName) {
let name = [];
if (mcVersion <= MC_VERSION.v1_11) {
tags.push(`CustomName:"${customName}"`);
} else if (mcVersion == MC_VERSION.v1_13) {
name.push(getName());
name.push(getNameColor());
name.push(getNameBold());
name.push(getNameItalic());
name.push(getNameObfuscated());
name.push(getNameStrikethrough());
tags.push(`CustomName:"{${name.join("")}}"`);
} else {
// CustomNames from 1.14+ can now use single quotes to contain json
// Replace escaped double quotes with single quotes to make it look pretty
name.push(getName().replaceAll("\\", ""));
name.push(getNameColor().replaceAll("\\", ""));
name.push(getNameBold().replaceAll("\\", ""));
name.push(getNameItalic().replaceAll("\\", ""));
name.push(getNameObfuscated().replaceAll("\\", ""));
name.push(getNameStrikethrough().replaceAll("\\", ""));
tags.push(`CustomName:'{${name.join("")}}'`);
}
}
if(showCustomName)
tags.push("CustomNameVisible:1b");
//Scoreboard tags
if (scoreboardTags) { if (scoreboardTags) {
const tagsList = scoreboardTags.split(','); const tagsList = scoreboardTags.split(',');
if (!tagsList[tagsList.length - 1].trim()) if (!tagsList[tagsList.length - 1].trim()) {
tagsList.pop(); tagsList.pop();
}
for (let i = 0; i < tagsList.length; i++)
tagsList[i] = `"${tagsList[i].trim()}"`;
tags.push(`Tags:[${tagsList.join(",")}]`); tags.Tags = tagsList.map(value => value.trim());
} }
//DisabledSlots // Generate the final command
if(useDisabledSlots){ if (give && mcVersion > MC_VERSION.v1_16) {
tags.push("DisabledSlots:"+calculateDisabledSlotsFlag()); tags.id = "minecraft:armor_stand";
} }
//Now the pose const parsedTags = NBT.stringify(tags);
var pose = [];
if(!isZero(body))
pose.push("Body:"+getJSONArray(body));
if(!isZero(head))
pose.push("Head:"+getJSONArray(head));
if(!isZero(leftLeg))
pose.push("LeftLeg:"+getJSONArray(leftLeg));
if(!isZero(rightLeg))
pose.push("RightLeg:"+getJSONArray(rightLeg));
if(showArms){
if(!isZero(leftArm))
pose.push("LeftArm:"+getJSONArray(leftArm));
if(!isZero(rightArm))
pose.push("RightArm:"+getJSONArray(rightArm));
}
if(pose.length > 0)
tags.push("Pose:{"+pose.join(",")+"}");
code += tags.join(",");
code += "}";
if (give) { if (give) {
code += "}"; let command = "/give @p ";
if (mcVersion > MC_VERSION.v_11) {
code += " 1"
}
}
return code;
}
function getHandRightItem(){
if(equipHandRight == "") return "{}";
return "{id:\""+equipHandRight+"\",Count:1b}";
}
function getHandLeftItem(){
if(equipHandLeft == "") return "{}";
return "{id:\""+equipHandLeft+"\",Count:1b}";
}
function getShoesItem(){
if(equipShoes == "") return "{}";
return "{id:\""+equipShoes+"\",Count:1b"
+getLeatherColorString($("#shoecolor"), isLeatherArmor(equipShoes))
+"}";
}
function getLeggingsItem(){
if(equipLeggings == "") return "{}";
return "{id:\""+equipLeggings+"\",Count:1b"
+getLeatherColorString($("#leggingscolor"), isLeatherArmor(equipLeggings))
+"}";
}
function getChestplateItem(){
if(equipChestplate == "") return "{}";
return "{id:\""+equipChestplate+"\",Count:1b"
+getLeatherColorString($("#chestplatecolor"), isLeatherArmor(equipChestplate))
+"}";
}
function getHeadItem(){
if(equipHelmet == "") return "{}";
// Use input as item
if(equipCustomHeadMode == "item"){
return "{id:\""+equipHelmet+"\",Count:1b"
+getLeatherColorString($("#helmetcolor"), isLeatherArmor(equipHelmet))
+"}";
}
// Use input as player name
else if(equipCustomHeadMode == "player"){
if (mcVersion <= MC_VERSION.v1_11) { if (mcVersion <= MC_VERSION.v1_11) {
return "{id:\"skull\",Count:1b,Damage:3b,tag:{SkullOwner:\""+equipHelmet+"\"}}"; return command + "minecraft:armor_stand 1 0 {EntityTag:" + parsedTags + "}";
} else if (mcVersion <= MC_VERSION.v1_16) {
return command + "minecraft:armor_stand{EntityTag:" + parsedTags + "} 1"
} else { } else {
return "{id:\"player_head\",Count:1b,tag:{SkullOwner:\""+equipHelmet+"\"}}"; let code = "minecraft:armor_stand[minecraft:entity_data=" + parsedTags;
if (customName && mcVersion >= MC_VERSION.v1_21) {
code += `,minecraft:custom_name=${generateCustomName()}`
}
return command + code + "] 1";
}
} else {
if (mcVersion <= MC_VERSION.v1_9) {
// Old entity name
return "/summon ArmorStand ~ ~ ~ " + parsedTags;
} else if (mcVersion < MC_VERSION.v1_13) {
return "/summon minecraft:armor_stand ~ ~ ~ " + parsedTags;
} else {
// In 1.13, positions are no longer center-corrected.
// Adding .5 makes it centered. However for players it is already center-corrected
let position = centercorrected ? "~ ~-0.5 ~" : "~ ~ ~";
return "/summon minecraft:armor_stand " + position + " " + parsedTags;
} }
} }
}
// Use input as url function generateCustomName() {
// Best reference: http://redd.it/24quwx if (mcVersion <= MC_VERSION.v1_11) {
else if(equipCustomHeadMode == "url"){ // Versions less than 1.12 did not support styles
var base64Value = btoa('{"textures":{"SKIN":{"url":"'+equipHelmet+'"}}}'); return customName;
if (mcVersion <= MC_VERSION.v1_11) {
return '{id:"skull",Count:1b,Damage:3b,tag:{SkullOwner:{Id:"'+generateUUID()+'",Properties:{textures:[{Value:"'+base64Value+'"}]}}}}';
} else if (mcVersion <= MC_VERSION.v1_14) {
return '{id:"minecraft:player_head",Count:1b,tag:{SkullOwner:{Id:"'+generateUUID()+'",Properties:{textures:[{Value:"'+base64Value+'"}]}}}}';
} else {
return '{id:"minecraft:player_head",Count:1b,tag:{SkullOwner:{Id:'+generateIntArray()+',Properties:{textures:[{Value:"'+base64Value+'"}]}}}}';
}
} }
// Parse give code let props = {};
else if(equipCustomHeadMode == "givecode"){
// Give Code in this format: /give @p skull 1 3 {display:{Name:"Some Name"},SkullOwner:{Id:"a74719ce...
// Give code in 1.13 has changed to this format: /give @p player_head{display:{Name:"Some Name"},SkullOwner:{Id:"a74719ce...
if(equipHelmet.indexOf("SkullOwner:{") >= 0){
var skullOwnerRaw = equipHelmet.substring(equipHelmet.indexOf("SkullOwner"));
var parsed = "";
var bracketCounter = 0;
var bracketsStarted = false;
for(var i = 0; i < skullOwnerRaw.length; i++){ if (customName) {
var c = skullOwnerRaw[i]; props.text = customName;
}
if(c == "{") bracketCounter++; if (nameColor) {
if(c == "}") bracketCounter--; props.color = nameColor;
}
parsed += c; if (nameBold) {
if(bracketCounter == 0 && bracketsStarted) break; props.bold = nameBold;
if(c == ":") bracketsStarted = true; }
if (nameItalic) {
props.italic = nameItalic;
}
if (nameStrikethrough) {
props.strikethrough = nameStrikethrough;
}
if (nameObfuscated) {
props.obfuscated = nameObfuscated;
}
let stringified = JSON.stringify(props);
if (mcVersion < MC_VERSION.v1_14) {
// Stringify again to escape double quotes, as versions below 1.14
// did not have the ability to use single quotes as strings
return new NBTRaw(JSON.stringify(stringified));
}
return new NBTRaw("'" + stringified + "'");
}
function generateArmorItems() {
function generateArmorItem(armorID) {
if (armorID === "") {
return {};
}
let data = {
id: armorID,
Count: 1,
}
// If the armor is leather, then apply its color
if (isLeatherArmor(armorID)) {
const element = $(`#${armorID.substring(8) + "_color"}`);
const color = getDecimalRGB(element.css("background-color"));
if (mcVersion >= MC_VERSION.v1_20_5) {
data.components = {
dyed_color: { rgb: color }
};
} else {
data.tag = {
display: {
color: color
}
};
}
}
return data;
}
// Equipments (aka ArmorItems) are order-sensitive
// and must follow the order: feet, legs, chest, head
let items = [
generateArmorItem(equipShoes),
generateArmorItem(equipLeggings),
generateArmorItem(equipChestplate),
];
// Handle different items in the head slot
if (equipHelmet == "") {
items.push({});
return items;
}
switch (equipCustomHeadMode) {
case "item":
items.push(generateArmorItem(equipHelmet));
break;
// Head is a player head with the given name
case "player": {
let data = {
id: (mcVersion <= MC_VERSION.v1_11) ? "skull" : "player_head",
Count: 1,
};
if (mcVersion <= MC_VERSION.v1_11) {
data.Damage = 3;
} }
if (mcVersion <= MC_VERSION.v1_11) { if (mcVersion <= MC_VERSION.v1_16) {
return '{id:"skull",Count:1b,Damage:3b,tag:{'+parsed+'}}'; data.tag = { SkullOwner: equipHelmet };
} else { } else {
return '{id:"player_head",Count:1b,tag:{'+parsed+'}}'; data.components = {
} profile: { name: equipHelmet }
} };
// Give Code in this format: /give @p skull 1 3 {SkullOwner:"playername"} (quotes optional)
// Give code in 1.13 has changed to this format: /give @p player_head{display:{Name:"Some Name"},SkullOwner:{Id"a74719ce...
else{
var skullOwnerRaw = equipHelmet.substring(equipHelmet.indexOf("SkullOwner:"));
skullOwnerRaw = skullOwnerRaw.substring(0, skullOwnerRaw.indexOf("}"));
if (mcVersion <= MC_VERSION.v1_11) {
return '{id:"skull",Count:1b,Damage:3b,tag:{'+skullOwnerRaw+'}}';
} else {
return '{id:"player_head",Count:1b,tag:{'+skullOwnerRaw+'}}';
} }
items.push(data);
break;
} }
case "url": {
// Best reference: http://redd.it/24quwx
const base64Value = btoa(JSON.stringify({
textures: {
SKIN: { url: equipHelmet }
}
}));
const innerNBT = {
Id: (mcVersion >= MC_VERSION.v1_16) ? generateIntArray() : generateUUID(),
Properties: {
textures: [{ Value: base64Value }]
}
};
const data = {
id: "minecraft:player_head",
Count: 1
};
if (mcVersion >= MC_VERSION.v1_20_5) {
data.components = {
profile: innerNBT
};
} else {
data.tag = {
SkullOwner: innerNBT
};
if (mcVersion < MC_VERSION.v1_13) {
data.id = "skull";
data.Damage = 3;
}
}
items.push(data);
break;
}
case "givecode": {
// For now, assume the give code is from minecraft-heads
// TODO Parser to read any valid give codes
const data = {
id: "minecraft:player_head",
Count: 1
};
if (mcVersion < MC_VERSION.v1_13) {
data.id = "skull";
data.Damage = 3;
data.tag = new NBTRaw(equipHelmet.trim().slice(29));
} else if (mcVersion < MC_VERSION.v1_20_5) {
data.tag = new NBTRaw(equipHelmet.trim().slice(30, -2));
} else {
// Extracting the entity data for versions greater than 1.20.5 requires more work.
// Format of the give code that is generated from minecraft-heads:
// /give @p minecraft:player_head[minecraft:custom_name='...', minecraft:lore=[...], profile={...}] 1
const nameIndex = equipHelmet.indexOf("minecraft:custom_name=");
const loreIndex = equipHelmet.indexOf("minecraft:lore=");
const profileIndex = equipHelmet.indexOf("profile=");
const customName = equipHelmet.slice(nameIndex + 22, loreIndex - 1);
const lore = equipHelmet.slice(loreIndex + 15, profileIndex - 1);
const profile = equipHelmet.slice(profileIndex + 8, -3);
data.components = {
custom_name: new NBTRaw(customName),
lore: new NBTRaw(lore),
profile: new NBTRaw(profile),
};
}
items.push(data);
break;
}
} }
return items;
} }
function getName() { function generateHandItems() {
if (!customName) return "" function generateHandItem(item) {
return `\\"text\\":\\"${customName}\\"` if (item == "") {
return {};
}
return {
id: item,
Count: 1
};
}
// HandItems must follow the order: Main hand (right hand), Off hand (left hand)
return [
generateHandItem(equipHandRight),
generateHandItem(equipHandLeft),
];
} }
function getNameColor() { function generatePose() {
if (nameColor == "") return "" const pose = {};
return `,\\"color\\":\\"${nameColor}\\"`
}
function getNameBold() { if (!isZero(body)) {
if (!nameBold) return "" pose.Body = toNBTFloatArray(body);
return `,\\"bold\\":\\"true\\"` }
}
function getNameItalic() { if (!isZero(head)) {
if (!nameItalic) return "" pose.Head = toNBTFloatArray(head);
return `,\\"italic\\":\\"true\\"` }
}
function getNameStrikethrough() { if (!isZero(leftLeg)) {
if (!nameStrikethrough) return "" pose.LeftLeg = toNBTFloatArray(leftLeg);
return `,\\"strikethrough\\":\\"true\\"` }
}
function getNameObfuscated() { if (!isZero(rightLeg)) {
if (!nameObfuscated) return "" pose.RightLeft = toNBTFloatArray(rightLeg);
return `,\\"obfuscated\\":\\"true\\"` }
if (showArms) {
if (!isZero(leftArm)) {
pose.LeftArm = toNBTFloatArray(leftArm);
}
if (!isZero(rightArm)) {
pose.RightArm = toNBTFloatArray(rightArm);
}
}
return (Object.keys(pose).length > 0) ? pose : null;
} }
function calculateDisabledSlotsFlag() { function calculateDisabledSlotsFlag() {
@ -887,11 +942,12 @@ function calculateDisabledSlotsFlag() {
return result; return result;
} }
function isZero(vector){ function isZero(vector) {
return vector.x == 0 && vector.y == 0 && vector.z == 0; return vector.x == 0 && vector.y == 0 && vector.z == 0;
} }
function getJSONArray(vector){
return "["+vector.x+"f,"+vector.y+"f,"+vector.z+"f]"; function toNBTFloatArray(vector) {
return [ new NBTFloat(vector.x), new NBTFloat(vector.y), new NBTFloat(vector.z) ];
} }
function getMouseDeltaX(){ function getMouseDeltaX(){
@ -912,27 +968,23 @@ function getMouseDeltaY(){
function render(){ function render(){
renderer.render(scene, camera); renderer.render(scene, camera);
var deltaTime = clock.getDelta();
armorstandWrapper.rotation.y = rotY + getMouseDeltaX(); armorstandWrapper.rotation.y = rotY + getMouseDeltaX();
armorstandWrapper.rotation.x = rotX + getMouseDeltaY(); armorstandWrapper.rotation.x = rotX + getMouseDeltaY();
requestAnimationFrame(render); requestAnimationFrame(render);
} }
// ---- Additional functions // ---- Additional functions
// From here: http://stackoverflow.com/a/8809472/1456971 function generateUUID() {
function generateUUID(){ // From here: http://stackoverflow.com/a/8809472/1456971
var d = new Date().getTime(); var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0; var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16); d = Math.floor(d/16);
return (c=='x' ? r : (r&0x3|0x8)).toString(16); return (c=='x' ? r : (r&0x3|0x8)).toString(16);
}); });
return uuid; return uuid;
} }
@ -950,7 +1002,7 @@ function generateIntArray() {
ints.push(UUID.getInt32(i*4)); ints.push(UUID.getInt32(i*4));
} }
return '[I;' + ints.join(",") + ']'; return new NBTArrayInt(ints);
} }
function getDecimalRGB(rgb){ function getDecimalRGB(rgb){
@ -967,19 +1019,8 @@ function getDecimalRGB(rgb){
return (r << 16) | (g << 8) | b; return (r << 16) | (g << 8) | b;
} }
function isLeatherArmor(item){ function isLeatherArmor(item) {
if(item == null) return item ? item.startsWith("leather_") : false;
return false;
return item.indexOf("leather") == 0;
}
// Pass the colorpicker element as element. If condition is true, it will return a proper datatag for use in items, otherwise it will return an empty string.
function getLeatherColorString(element, condition){
if(condition){
var rgb = getDecimalRGB(element.css("background-color"));
return ",tag:{display:{color:"+rgb+"}}";
}
return "";
} }
// Rotate three.js mesh to fit the minecraft rotation // Rotate three.js mesh to fit the minecraft rotation
@ -1046,10 +1087,10 @@ function saveData() {
helmet_specifies: $("#equipCustomHeadMode").val(), helmet_specifies: $("#equipCustomHeadMode").val(),
leather_colours: { leather_colours: {
helmet: $(`#helmetcolor`).css(`background-color`), helmet: $(`#helmet_color`).css(`background-color`),
chestplate: $(`#chestplatecolor`).css(`background-color`), chestplate: $(`#chestplate_color`).css(`background-color`),
leggings: $(`#leggingscolor`).css(`background-color`), leggings: $(`#leggings_color`).css(`background-color`),
boots: $(`#shoecolor`).css(`background-color`) boots: $(`#boots_color`).css(`background-color`)
} }
}, },
@ -1160,15 +1201,10 @@ function loadData(data) {
$(`input[name=equipHandLeft]`).val(data.equipment.hands.left); $(`input[name=equipHandLeft]`).val(data.equipment.hands.left);
$(`#equipCustomHeadMode`).val(data.equipment.helmet_specifies); $(`#equipCustomHeadMode`).val(data.equipment.helmet_specifies);
$(`#helmetcolor`).css(`background-color`, data.equipment.leather_colours.helmet); $(`#helmet_color`).css(`background-color`, data.equipment.leather_colours.helmet);
$(`#chestplatecolor`).css(`background-color`, data.equipment.leather_colours.chestplate); $(`#chestplate_color`).css(`background-color`, data.equipment.leather_colours.chestplate);
$(`#leggingscolor`).css(`background-color`, data.equipment.leather_colours.leggings); $(`#leggings_color`).css(`background-color`, data.equipment.leather_colours.leggings);
$(`#shoecolor`).css(`background-color`, data.equipment.leather_colours.boots); $(`#boots_color`).css(`background-color`, data.equipment.leather_colours.boots);
getLeatherColorString($("#helmetcolor"))
getLeatherColorString($("#chestplatecolor"))
getLeatherColorString($("#leggingscolor"))
getLeatherColorString($("#shoecolor"))
//custom name //custom name
$(`#customname`).val(data.custom_name.name); $(`#customname`).val(data.custom_name.name);
@ -1206,7 +1242,6 @@ function loadData(data) {
$(`#pW`).prop(`checked`, data.lock_slot_interaction.place.weapons); $(`#pW`).prop(`checked`, data.lock_slot_interaction.place.weapons);
handleInput(); handleInput();
//console.log(`done loading!`)
} catch (err) { } catch (err) {
console.error(err); console.error(err);
alert(`An error occurred while loading the creation.`); alert(`An error occurred while loading the creation.`);
@ -1220,3 +1255,76 @@ function deleteSave(data) {
loadScreen(); loadScreen();
alert(`${data} has been deleted!`); alert(`${data} has been deleted!`);
}; };
// Define NBT classes for properly stringifying JS objects to NBT
class NBTObject {};
class NBTRaw extends NBTObject {
constructor(raw) {
super();
this.raw = raw;
}
toString() {
return this.raw;
}
}
class NBTFloat extends NBTObject {
constructor(n) {
super();
this.n = n;
}
toString() {
return this.n + "f";
}
}
class NBTArrayInt extends NBTObject {
constructor(arr) {
super();
this.arr = arr;
}
toString() {
return "[I;" + this.arr.join(",") + "]";
}
};
class NBT {
static stringify(object) {
const type = typeof object;
switch (type) {
case "string":
return '"' + object + '"';
case "number":
case "boolean":
return new String(object);
case "object": {
if (Array.isArray(object)) {
return "[" +
object
.filter(value => value != null)
.map(value => NBT.stringify(value))
.join(",")
+ "]";
}
if (object instanceof NBTObject) {
return object.toString();
}
// Fallback (javascript object)
return "{" +
Object.entries(object)
.filter(([key, value]) => value != null)
.map(([key, value]) => key + ":" + NBT.stringify(value))
.join(",")
+ "}";
}
}
}
}