mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Animated Texture Support
This commit is contained in:
1 parent
988dbe0cd2
commit
de2fcea154
17 files changed
+578
-21
No files matched your search
@@ -830,6 +830,15 @@ public abstract class Ability implements IAbility, IAbilityAction {
|
||||
addIconLayerFields(defs, icon, 0, isCustom);
|
||||
addIconLayerFields(defs, icon, 1, () -> isCustom.getAsBoolean() && icon.getLayerCount() >= 2);
|
||||
addIconLayerFields(defs, icon, 2, () -> isCustom.getAsBoolean() && icon.getLayerCount() >= 3);
|
||||
|
||||
// Animation
|
||||
defs.add(FieldDef.section("Animation").tab("Effects").visibleWhen(isCustom));
|
||||
defs.add(FieldDef.boolField("gui.animated", icon::isAnimated, icon::setAnimated)
|
||||
.tab("Effects").visibleWhen(isCustom).hover("gui.animated.hover"));
|
||||
defs.add(FieldDef.intField("gui.frameCount", icon::getFrameCount, icon::setFrameCount)
|
||||
.tab("Effects").range(1, 256).visibleWhen(() -> isCustom.getAsBoolean() && icon.isAnimated()));
|
||||
defs.add(FieldDef.intField("gui.frameTime", icon::getFrameTime, icon::setFrameTime)
|
||||
.tab("Effects").range(1, 100).visibleWhen(() -> isCustom.getAsBoolean() && icon.isAnimated()));
|
||||
}
|
||||
|
||||
// External field providers (e.g., DBC Addon injecting a "DBC" tab)
|
||||
|
||||
@@ -32,6 +32,11 @@ public class AbilityIconData {
|
||||
/** Per-state icon UV overrides for layer 0 (0-indexed, maps to toggle states 1, 2, ...). */
|
||||
private int[][] stateIcons = null;
|
||||
|
||||
// Animation
|
||||
private boolean animated = false;
|
||||
private int frameCount = 1;
|
||||
private int frametime = 2;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// LAYER INNER CLASS
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
@@ -125,6 +130,10 @@ public class AbilityIconData {
|
||||
}
|
||||
nbt.setTag("Layers", layerList);
|
||||
|
||||
nbt.setBoolean("Animated", animated);
|
||||
nbt.setInteger("FrameCount", frameCount);
|
||||
nbt.setInteger("FrameTime", frametime);
|
||||
|
||||
if (stateIcons != null && stateIcons.length > 0) {
|
||||
NBTTagList list = new NBTTagList();
|
||||
for (int[] pair : stateIcons) {
|
||||
@@ -150,6 +159,13 @@ public class AbilityIconData {
|
||||
if (layerCount < 1) layerCount = 1;
|
||||
if (layerCount > MAX_LAYERS) layerCount = MAX_LAYERS;
|
||||
|
||||
if (nbt.hasKey("Animated"))
|
||||
animated = nbt.getBoolean("Animated");
|
||||
if (nbt.hasKey("FrameCount"))
|
||||
frameCount = Math.max(1, nbt.getInteger("FrameCount"));
|
||||
if (nbt.hasKey("FrameTime"))
|
||||
frametime = Math.max(1, nbt.getInteger("FrameTime"));
|
||||
|
||||
if (nbt.hasKey("Layers")) {
|
||||
NBTTagList layerList = nbt.getTagList("Layers", 10);
|
||||
for (int i = 0; i < MAX_LAYERS; i++) {
|
||||
@@ -256,6 +272,37 @@ public class AbilityIconData {
|
||||
save();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// ANIMATION
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
public boolean isAnimated() {
|
||||
return animated;
|
||||
}
|
||||
|
||||
public void setAnimated(boolean animated) {
|
||||
this.animated = animated;
|
||||
save();
|
||||
}
|
||||
|
||||
public int getFrameCount() {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
public void setFrameCount(int frameCount) {
|
||||
this.frameCount = Math.max(1, frameCount);
|
||||
save();
|
||||
}
|
||||
|
||||
public int getFrameTime() {
|
||||
return frametime;
|
||||
}
|
||||
|
||||
public void setFrameTime(int frametime) {
|
||||
this.frametime = Math.max(1, frametime);
|
||||
save();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// PER-STATE ICON OVERRIDES (applies to layer 0)
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.minecraft.util.ResourceLocation;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.gui.global.GuiNPCManageEffects;
|
||||
import noppes.npcs.client.gui.script.GuiScriptInterface;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.gui.util.GuiMenuTopButton;
|
||||
import noppes.npcs.client.gui.util.GuiNpcButtonYesNo;
|
||||
import noppes.npcs.client.gui.util.GuiNpcLabel;
|
||||
@@ -129,6 +130,12 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
|
||||
int width = data.getTotalWidth();
|
||||
int height = data.getTotalHeight();
|
||||
|
||||
// Animation V offset for preview
|
||||
if (data.isAnimated()) {
|
||||
iconY += (int) (data.getCurrentFrameVOffset() * height);
|
||||
} else if (effect.animated && effect.frameCount > 1) {
|
||||
iconY += (int) (AnimationHelper.getFrameVOffset(height, effect.frameCount, effect.frametime) * height);
|
||||
}
|
||||
|
||||
func_152125_a(x, y, iconX, iconY, iconWidth, iconHeight, iconRenderSize, iconRenderSize, width, height);
|
||||
|
||||
@@ -183,6 +190,33 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
|
||||
effect.height
|
||||
));
|
||||
scrollWindow.addLabel(new GuiNpcLabel(9, "effect.editor.height", scrollWindow.getTextField(9).xPosition - 43, y + 6, 0xFFFFFF));
|
||||
y += 25;
|
||||
|
||||
// Animation controls
|
||||
scrollWindow.addLabel(new GuiNpcLabel(11, "gui.animated", x, y + 5, 0xFFFFFF));
|
||||
GuiNpcButtonYesNo animBtn = new GuiNpcButtonYesNo(11, x + 70, y, 50, 20, effect.animated);
|
||||
animBtn.setHoverText("gui.animated.hover");
|
||||
scrollWindow.addButton(animBtn);
|
||||
|
||||
if (effect.animated) {
|
||||
y += 23;
|
||||
scrollWindow.addLabel(new GuiNpcLabel(12, "gui.frameCount", x, y + 6, 0xFFFFFF));
|
||||
scrollWindow.addTextField(setIntegerOnly(
|
||||
new GuiNpcTextField(12, this, x + 80, y, 60, 20, "" + effect.frameCount),
|
||||
1,
|
||||
256,
|
||||
effect.frameCount
|
||||
));
|
||||
|
||||
y += 23;
|
||||
scrollWindow.addLabel(new GuiNpcLabel(13, "gui.frameTime", x, y + 6, 0xFFFFFF));
|
||||
scrollWindow.addTextField(setIntegerOnly(
|
||||
new GuiNpcTextField(13, this, x + 80, y, 60, 20, "" + effect.frametime),
|
||||
1,
|
||||
100,
|
||||
effect.frametime
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -209,6 +243,11 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
|
||||
GuiNpcButtonYesNo button = (GuiNpcButtonYesNo) guibutton;
|
||||
effect.lossOnDeath = button.getBoolean();
|
||||
}
|
||||
if (id == 11) {
|
||||
GuiNpcButtonYesNo button = (GuiNpcButtonYesNo) guibutton;
|
||||
effect.animated = button.getBoolean();
|
||||
initGui();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,6 +287,12 @@ public class SubGuiEffectGeneral extends SubGuiInterface implements ITextfieldLi
|
||||
case 9:
|
||||
effect.height = guiNpcTextField.getInteger();
|
||||
break;
|
||||
case 12:
|
||||
effect.frameCount = Math.max(1, guiNpcTextField.getInteger());
|
||||
break;
|
||||
case 13:
|
||||
effect.frametime = Math.max(1, guiNpcTextField.getInteger());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +193,14 @@ public class SubGuiChainedAbilityConfig extends SubGuiInterface implements IText
|
||||
.tab("Icon").visibleWhen(layerVisible));
|
||||
}
|
||||
|
||||
// Animation
|
||||
fieldDefs.add(FieldDef.section("Animation").tab("Icon"));
|
||||
fieldDefs.add(FieldDef.boolField("gui.animated", chainIcon::isAnimated, chainIcon::setAnimated).tab("Icon").hover("gui.animated.hover"));
|
||||
fieldDefs.add(FieldDef.intField("gui.frameCount", chainIcon::getFrameCount, chainIcon::setFrameCount)
|
||||
.tab("Icon").range(1, 256).visibleWhen(chainIcon::isAnimated));
|
||||
fieldDefs.add(FieldDef.intField("gui.frameTime", chainIcon::getFrameTime, chainIcon::setFrameTime)
|
||||
.tab("Icon").range(1, 100).visibleWhen(chainIcon::isAnimated));
|
||||
|
||||
// External field providers (e.g., DBC Addon injecting a "DBC" tab)
|
||||
if (AbilityController.Instance != null) {
|
||||
for (IChainedAbilityFieldProvider provider : AbilityController.Instance.getChainedFieldProviders()) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import noppes.npcs.api.gui.ICustomGuiComponent;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.gui.custom.GuiCustom;
|
||||
import noppes.npcs.client.gui.custom.interfaces.IGuiComponent;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.renderer.ImageData;
|
||||
import noppes.npcs.scripted.gui.ScriptGuiTexturedRect;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -30,6 +31,11 @@ public class CustomGuiTexturedRect extends Gui implements IGuiComponent {
|
||||
float alpha;
|
||||
float rotation;
|
||||
|
||||
// Animation
|
||||
boolean animated;
|
||||
int frameCount = 1;
|
||||
int frametime = 2;
|
||||
|
||||
public CustomGuiTexturedRect(int id, String texture, int x, int y, int width, int height) {
|
||||
this(id, texture, x, y, width, height, 0, 0);
|
||||
}
|
||||
@@ -64,10 +70,18 @@ public class CustomGuiTexturedRect extends Gui implements IGuiComponent {
|
||||
int totalWidth = this.imageData.getTotalWidth();
|
||||
int totalHeight = this.imageData.getTotalHeight();
|
||||
|
||||
// Animation V offset
|
||||
float vOff = 0f;
|
||||
if (this.imageData.isAnimated()) {
|
||||
vOff = this.imageData.getCurrentFrameVOffset();
|
||||
} else if (this.animated && this.frameCount > 1) {
|
||||
vOff = AnimationHelper.getFrameVOffset(totalHeight, this.frameCount, this.frametime);
|
||||
}
|
||||
|
||||
float u1 = (float) textureX / (float) totalWidth;
|
||||
float u2 = u1 + (float) width / (float) totalWidth;
|
||||
float v1 = (float) textureY / (float) totalHeight;
|
||||
float v2 = v1 + (float) height / (float) totalHeight;
|
||||
float v1 = vOff + (float) textureY / (float) totalHeight;
|
||||
float v2 = vOff + ((float) textureY + (float) height) / (float) totalHeight;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
float red = (color >> 16 & 255) / 255f;
|
||||
@@ -104,6 +118,9 @@ public class CustomGuiTexturedRect extends Gui implements IGuiComponent {
|
||||
component.setColor(color);
|
||||
component.setAlpha(alpha);
|
||||
component.setRotation(rotation);
|
||||
if (this.animated && this.frameCount > 1) {
|
||||
component.setAnimation(this.frameCount, this.frametime);
|
||||
}
|
||||
return component;
|
||||
}
|
||||
|
||||
@@ -124,6 +141,12 @@ public class CustomGuiTexturedRect extends Gui implements IGuiComponent {
|
||||
rect.alpha = component.getAlpha();
|
||||
rect.rotation = component.getRotation();
|
||||
|
||||
if (component.isAnimated()) {
|
||||
rect.animated = true;
|
||||
rect.frameCount = component.getFrameCount();
|
||||
rect.frametime = component.getFrameTime();
|
||||
}
|
||||
|
||||
return rect;
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.renderer.ImageData;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
@@ -145,10 +146,18 @@ public class AbilityIcon extends Gui {
|
||||
int ix = (state > 0) ? data.getIconXForState(state) : layer.iconX;
|
||||
int iy = (state > 0) ? data.getIconYForState(state) : layer.iconY;
|
||||
|
||||
// Animation V offset
|
||||
float vOff = 0f;
|
||||
if (imageData.isAnimated()) {
|
||||
vOff = imageData.getCurrentFrameVOffset();
|
||||
} else if (data.isAnimated()) {
|
||||
vOff = AnimationHelper.getFrameVOffset((int) texH, data.getFrameCount(), data.getFrameTime());
|
||||
}
|
||||
|
||||
float u1 = ix / texW;
|
||||
float v1 = iy / texH;
|
||||
float v1 = vOff + iy / texH;
|
||||
float u2 = (ix + data.width) / texW;
|
||||
float v2 = (iy + data.height) / texH;
|
||||
float v2 = vOff + (iy + data.height) / texH;
|
||||
|
||||
Tessellator t = Tessellator.instance;
|
||||
t.startDrawingQuads();
|
||||
|
||||
@@ -10,6 +10,7 @@ import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.CustomNpcResourceListener;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.gui.SubGuiColorSelector;
|
||||
import noppes.npcs.client.gui.global.GuiNPCManageLinked;
|
||||
import noppes.npcs.client.gui.script.GuiScriptInterface;
|
||||
@@ -286,6 +287,31 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
|
||||
scrollWindow.addLabel(new GuiNpcLabel(70, "display.durabilityShow", x, localY + 5, 0xFFFFFF));
|
||||
GuiNpcButtonYesNo durabilityButton = new GuiNpcButtonYesNo(26, x + 120, localY, linkedItem.display.durabilityShow);
|
||||
scrollWindow.addButton(durabilityButton);
|
||||
localY += 30;
|
||||
|
||||
// Animation controls
|
||||
scrollWindow.addLabel(new GuiNpcLabel(80, "gui.animated", x, localY + 5, 0xFFFFFF));
|
||||
boolean isAnim = linkedItem.display.animated != null && linkedItem.display.animated;
|
||||
GuiNpcButtonYesNo animBtn = new GuiNpcButtonYesNo(29, x + 120, localY, isAnim);
|
||||
animBtn.setHoverText("gui.animated.hover");
|
||||
scrollWindow.addButton(animBtn);
|
||||
localY += 25;
|
||||
|
||||
if (isAnim) {
|
||||
scrollWindow.addLabel(new GuiNpcLabel(81, "gui.frameCount", x, localY + 5, 0xFFFFFF));
|
||||
GuiNpcTextField fcField = new GuiNpcTextField(71, this, x + 120, localY, 60, 20,
|
||||
"" + (linkedItem.display.frameCount != null ? linkedItem.display.frameCount : 1));
|
||||
fcField.setIntegersOnly().setMinMaxDefault(1, 256, 1);
|
||||
scrollWindow.addTextField(fcField);
|
||||
localY += 25;
|
||||
|
||||
scrollWindow.addLabel(new GuiNpcLabel(82, "gui.frameTime", x, localY + 5, 0xFFFFFF));
|
||||
GuiNpcTextField ftField = new GuiNpcTextField(72, this, x + 120, localY, 60, 20,
|
||||
"" + (linkedItem.display.frametime != null ? linkedItem.display.frametime : 2));
|
||||
ftField.setIntegersOnly().setMinMaxDefault(1, 100, 2);
|
||||
scrollWindow.addTextField(ftField);
|
||||
localY += 25;
|
||||
}
|
||||
|
||||
scrollWindow.scrollY = 0;
|
||||
scrollWindow.maxScrollY = Math.max(localY - scrollWindow.clipHeight, 0);
|
||||
@@ -381,6 +407,9 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
|
||||
linkedItem.isNormalItem = ((GuiNpcButtonYesNo) guibutton).getBoolean();
|
||||
} else if (id == 26 && guibutton instanceof GuiNpcButtonYesNo) {
|
||||
linkedItem.display.durabilityShow = ((GuiNpcButtonYesNo) guibutton).getBoolean();
|
||||
} else if (id == 29 && guibutton instanceof GuiNpcButtonYesNo) {
|
||||
linkedItem.display.animated = ((GuiNpcButtonYesNo) guibutton).getBoolean();
|
||||
initGui();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +458,10 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
|
||||
linkedItem.display.translateY = textField.getFloat();
|
||||
} else if (id == 63) {
|
||||
linkedItem.display.translateZ = textField.getFloat();
|
||||
} else if (id == 71) {
|
||||
linkedItem.display.frameCount = Math.max(1, textField.getInteger());
|
||||
} else if (id == 72) {
|
||||
linkedItem.display.frametime = Math.max(1, textField.getInteger());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,7 +505,20 @@ public class SubGuiLinkedItem extends SubGuiInterface implements ITextfieldListe
|
||||
int iconWidth = data.getTotalWidth();
|
||||
int iconHeight = data.getTotalWidth();
|
||||
int width = data.getTotalWidth();
|
||||
int height = data.getTotalWidth();
|
||||
int height = data.getTotalHeight();
|
||||
|
||||
// Animation V offset for preview
|
||||
if (data.isAnimated()) {
|
||||
iconY += (int) (data.getCurrentFrameVOffset() * height);
|
||||
iconHeight = data.getFrameHeight();
|
||||
} else if (linkedItem.display.animated != null && linkedItem.display.animated
|
||||
&& linkedItem.display.frameCount != null && linkedItem.display.frameCount > 1) {
|
||||
iconY += (int) (AnimationHelper.getFrameVOffset(height, linkedItem.display.frameCount, linkedItem.display.frametime) * height);
|
||||
iconHeight = height / linkedItem.display.frameCount;
|
||||
} else {
|
||||
iconHeight = data.getTotalWidth();
|
||||
height = data.getTotalWidth();
|
||||
}
|
||||
|
||||
float[] colors = ColorUtil.hexToRGB(linkedItem.display.itemColor);
|
||||
GL11.glColor3f(colors[0], colors[1], colors[2]);
|
||||
|
||||
@@ -6,6 +6,7 @@ import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.renderer.ImageData;
|
||||
import noppes.npcs.controllers.data.CustomEffect;
|
||||
import noppes.npcs.controllers.data.PlayerEffect;
|
||||
@@ -82,6 +83,13 @@ public class GuiEffectBar extends GuiScreen {
|
||||
int texWidth = imageData.getTotalWidth();
|
||||
int texHeight = imageData.getTotalHeight();
|
||||
|
||||
// Animation V offset
|
||||
if (imageData.isAnimated()) {
|
||||
iconV += (int) (imageData.getCurrentFrameVOffset() * texHeight);
|
||||
} else if (entry.effect.animated && entry.effect.frameCount > 1) {
|
||||
iconV += (int) (AnimationHelper.getFrameVOffset(texHeight, entry.effect.frameCount, entry.effect.frametime) * texHeight);
|
||||
}
|
||||
|
||||
// Use iconYOffset to vertically center the icon
|
||||
func_152125_a(x + 2, drawY + iconYOffset, iconU, iconV, iconWidth, iconHeight, iconRenderSize, iconRenderSize, texWidth, texHeight);
|
||||
|
||||
@@ -191,7 +199,14 @@ public class GuiEffectBar extends GuiScreen {
|
||||
if (imageData != null && imageData.imageLoaded()) {
|
||||
GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
imageData.bindTexture();
|
||||
func_152125_a(tooltipX + (maxWidth - iconSize) / 2, textY + 4, hoveredEffect.iconX, hoveredEffect.iconY, hoveredEffect.getWidth(), hoveredEffect.getHeight(), iconSize, iconSize, imageData.getTotalWidth(), imageData.getTotalHeight());
|
||||
int tooltipIconV = hoveredEffect.iconY;
|
||||
int tooltipTexH = imageData.getTotalHeight();
|
||||
if (imageData.isAnimated()) {
|
||||
tooltipIconV += (int) (imageData.getCurrentFrameVOffset() * tooltipTexH);
|
||||
} else if (hoveredEffect.animated && hoveredEffect.frameCount > 1) {
|
||||
tooltipIconV += (int) (AnimationHelper.getFrameVOffset(tooltipTexH, hoveredEffect.frameCount, hoveredEffect.frametime) * tooltipTexH);
|
||||
}
|
||||
func_152125_a(tooltipX + (maxWidth - iconSize) / 2, textY + 4, hoveredEffect.iconX, tooltipIconV, hoveredEffect.getWidth(), hoveredEffect.getHeight(), iconSize, iconSize, imageData.getTotalWidth(), tooltipTexH);
|
||||
}
|
||||
|
||||
textureManager.bindTexture(specialIcons);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package noppes.npcs.client.renderer;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* Static utility for computing animated texture UV offsets.
|
||||
* Used by renderers when animation parameters come from data class fields
|
||||
* rather than .mcmeta auto-detection on ImageData.
|
||||
*/
|
||||
public class AnimationHelper {
|
||||
|
||||
/**
|
||||
* Returns the V-coordinate offset (0.0-1.0) for the current animation frame.
|
||||
* Based on system time for consistent sync across all animated textures.
|
||||
*
|
||||
* @param totalHeight Total pixel height of the sprite strip
|
||||
* @param frameCount Number of frames in the strip
|
||||
* @param frametime Ticks per frame (1 tick = 50ms)
|
||||
* @return V offset in 0.0-1.0 range, or 0.0 if not animated
|
||||
*/
|
||||
public static float getFrameVOffset(int totalHeight, int frameCount, int frametime) {
|
||||
if (frameCount <= 1 || totalHeight <= 0) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
frametime = Math.max(1, frametime);
|
||||
long millis = Minecraft.getSystemTime();
|
||||
int tick = (int) (millis / 50L);
|
||||
int frameIdx = (tick / frametime) % frameCount;
|
||||
int frameHeight = totalHeight / frameCount;
|
||||
|
||||
return (float) (frameIdx * frameHeight) / (float) totalHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the V-coordinate size of a single frame in 0.0-1.0 range.
|
||||
*
|
||||
* @param totalHeight Total pixel height of the sprite strip
|
||||
* @param frameCount Number of frames in the strip
|
||||
* @return V size of one frame, or 1.0 if not animated
|
||||
*/
|
||||
public static float getFrameVSize(int totalHeight, int frameCount) {
|
||||
if (frameCount <= 1 || totalHeight <= 0) {
|
||||
return 1f;
|
||||
}
|
||||
int frameHeight = totalHeight / frameCount;
|
||||
return (float) frameHeight / (float) totalHeight;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package noppes.npcs.client.renderer;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.texture.TextureManager;
|
||||
import net.minecraft.client.resources.IResource;
|
||||
@@ -11,6 +14,7 @@ import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class ImageData {
|
||||
private final ResourceLocation location;
|
||||
@@ -23,6 +27,14 @@ public class ImageData {
|
||||
private boolean gotWidthHeight;
|
||||
private boolean invalid;
|
||||
|
||||
// Animation fields
|
||||
private boolean animated = false;
|
||||
private int frameCount = 1;
|
||||
private int frametime = 2;
|
||||
private int[] frames = null;
|
||||
private boolean interpolate = false;
|
||||
private boolean mcmetaChecked = false;
|
||||
|
||||
public ImageData(String directory) {
|
||||
this.location = new ResourceLocation(directory);
|
||||
if (directory.startsWith("https://") || directory.startsWith("http://")) {
|
||||
@@ -141,6 +153,11 @@ public class ImageData {
|
||||
private void correctWidthHeight() {
|
||||
this.totalWidth = Math.max(this.totalWidth, 1);
|
||||
this.totalHeight = Math.max(this.totalHeight, 1);
|
||||
|
||||
if (!this.isUrl && !this.mcmetaChecked) {
|
||||
this.mcmetaChecked = true;
|
||||
tryLoadMcmeta();
|
||||
}
|
||||
}
|
||||
|
||||
public int getTotalWidth() {
|
||||
@@ -150,4 +167,112 @@ public class ImageData {
|
||||
public int getTotalHeight() {
|
||||
return this.gotWidthHeight ? this.totalHeight : -1;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// ANIMATION
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
private void tryLoadMcmeta() {
|
||||
try {
|
||||
ResourceLocation mcmetaLoc = new ResourceLocation(
|
||||
location.getResourceDomain(),
|
||||
location.getResourcePath() + ".mcmeta"
|
||||
);
|
||||
IResource resource = Minecraft.getMinecraft().getResourceManager().getResource(mcmetaLoc);
|
||||
InputStream stream = resource.getInputStream();
|
||||
try {
|
||||
InputStreamReader reader = new InputStreamReader(stream);
|
||||
JsonObject root = new JsonParser().parse(reader).getAsJsonObject();
|
||||
if (root.has("animation")) {
|
||||
JsonObject anim = root.getAsJsonObject("animation");
|
||||
this.animated = true;
|
||||
|
||||
if (anim.has("frametime")) {
|
||||
this.frametime = Math.max(1, anim.get("frametime").getAsInt());
|
||||
}
|
||||
if (anim.has("interpolate")) {
|
||||
this.interpolate = anim.get("interpolate").getAsBoolean();
|
||||
}
|
||||
if (anim.has("frames")) {
|
||||
JsonArray framesArray = anim.getAsJsonArray("frames");
|
||||
this.frames = new int[framesArray.size()];
|
||||
int maxFrame = 0;
|
||||
for (int i = 0; i < framesArray.size(); i++) {
|
||||
this.frames[i] = framesArray.get(i).getAsInt();
|
||||
if (this.frames[i] > maxFrame) {
|
||||
maxFrame = this.frames[i];
|
||||
}
|
||||
}
|
||||
this.frameCount = maxFrame + 1;
|
||||
} else if (this.totalWidth > 0) {
|
||||
this.frameCount = Math.max(1, this.totalHeight / this.totalWidth);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// No .mcmeta found or parse error — not animated
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically enable animation on this ImageData.
|
||||
* Used when animation settings come from data class fields rather than .mcmeta files.
|
||||
*/
|
||||
public void setAnimation(int frameCount, int frametime) {
|
||||
if (frameCount > 1) {
|
||||
this.animated = true;
|
||||
this.frameCount = Math.max(1, frameCount);
|
||||
this.frametime = Math.max(1, frametime);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAnimated() {
|
||||
return this.animated && this.frameCount > 1;
|
||||
}
|
||||
|
||||
public int getFrameCount() {
|
||||
return this.frameCount;
|
||||
}
|
||||
|
||||
public int getFrameTime() {
|
||||
return this.frametime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the pixel height of a single animation frame.
|
||||
* For non-animated textures, returns the total height.
|
||||
*/
|
||||
public int getFrameHeight() {
|
||||
if (this.animated && this.frameCount > 1) {
|
||||
return Math.max(1, this.totalHeight / this.frameCount);
|
||||
}
|
||||
return this.gotWidthHeight ? this.totalHeight : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the V-coordinate offset (0.0-1.0) for the current animation frame.
|
||||
* Based on system time so all instances of the same texture animate in sync.
|
||||
* Returns 0.0 for non-animated textures.
|
||||
*/
|
||||
public float getCurrentFrameVOffset() {
|
||||
if (!this.animated || this.frameCount <= 1 || this.totalHeight <= 0) {
|
||||
return 0f;
|
||||
}
|
||||
|
||||
long millis = Minecraft.getSystemTime();
|
||||
int tick = (int) (millis / 50L);
|
||||
int frameIdx;
|
||||
|
||||
if (this.frames != null && this.frames.length > 0) {
|
||||
frameIdx = (tick / this.frametime) % this.frames.length;
|
||||
frameIdx = this.frames[frameIdx];
|
||||
} else {
|
||||
frameIdx = (tick / this.frametime) % this.frameCount;
|
||||
}
|
||||
|
||||
int frameHeight = this.totalHeight / this.frameCount;
|
||||
return (float) (frameIdx * frameHeight) / (float) this.totalHeight;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import net.minecraftforge.client.IItemRenderer;
|
||||
import noppes.npcs.api.item.IItemCustomizable;
|
||||
import noppes.npcs.api.item.IItemStack;
|
||||
import noppes.npcs.client.ClientCacheHandler;
|
||||
import noppes.npcs.client.renderer.AnimationHelper;
|
||||
import noppes.npcs.client.renderer.ImageData;
|
||||
import noppes.npcs.items.ItemCustomizable;
|
||||
import noppes.npcs.scripted.NpcAPI;
|
||||
@@ -222,7 +223,8 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
int pass = 0;
|
||||
|
||||
GL11.glPushMatrix();
|
||||
ClientCacheHandler.getImageData(scriptCustomItem.getTexture()).bindTexture();
|
||||
ImageData entityImageData = ClientCacheHandler.getImageData(scriptCustomItem.getTexture());
|
||||
entityImageData.bindTexture();
|
||||
|
||||
if (renderInFrame) {
|
||||
GL11.glTranslatef(0.0F, -0.05F, 0.0F);
|
||||
@@ -233,10 +235,22 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
// Animation V offset for entity rendering
|
||||
float entityVOff = 0f;
|
||||
float entityVEnd = 1f;
|
||||
if (entityImageData.isAnimated()) {
|
||||
entityVOff = entityImageData.getCurrentFrameVOffset();
|
||||
entityVEnd = entityVOff + (float) entityImageData.getFrameHeight() / entityImageData.getTotalHeight();
|
||||
} else if (scriptCustomItem.isTextureAnimated() != null && scriptCustomItem.isTextureAnimated()
|
||||
&& scriptCustomItem.getFrameCount() != null && scriptCustomItem.getFrameCount() > 1) {
|
||||
entityVOff = AnimationHelper.getFrameVOffset(entityImageData.getTotalHeight(), scriptCustomItem.getFrameCount(), scriptCustomItem.getFrameTime());
|
||||
entityVEnd = entityVOff + AnimationHelper.getFrameVSize(entityImageData.getTotalHeight(), scriptCustomItem.getFrameCount());
|
||||
}
|
||||
|
||||
float f14 = 0.0F;
|
||||
float f15 = 1.0F;
|
||||
float f4 = 0.0F;
|
||||
float f5 = 1.0F;
|
||||
float f4 = entityVOff;
|
||||
float f5 = entityVEnd;
|
||||
float f6 = 1.0F;
|
||||
float f7 = 0.5F;
|
||||
float f8 = 0.25F;
|
||||
@@ -272,8 +286,11 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
GL11.glRotatef(-90.0F, 0.0F, 0.0F, 1.0F);
|
||||
}*/
|
||||
|
||||
ImageData imageData = ClientCacheHandler.getImageData(scriptCustomItem.getTexture());
|
||||
ItemRenderer.renderItemIn2D(tessellator, f15, f4, f14, f5, imageData.getTotalWidth(), imageData.getTotalHeight(), f9);
|
||||
ImageData loopImageData = ClientCacheHandler.getImageData(scriptCustomItem.getTexture());
|
||||
int entityFrameH = loopImageData.isAnimated() ? loopImageData.getFrameHeight()
|
||||
: (scriptCustomItem.isTextureAnimated() != null && scriptCustomItem.isTextureAnimated() && scriptCustomItem.getFrameCount() != null && scriptCustomItem.getFrameCount() > 1
|
||||
? loopImageData.getTotalHeight() / scriptCustomItem.getFrameCount() : loopImageData.getTotalHeight());
|
||||
ItemRenderer.renderItemIn2D(tessellator, f15, f4, f14, f5, loopImageData.getTotalWidth(), entityFrameH, f9);
|
||||
|
||||
if (itemStack.hasEffect(pass)) {
|
||||
GL11.glDepthFunc(GL11.GL_EQUAL);
|
||||
@@ -359,8 +376,20 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
GL11.glDisable(GL11.GL_LIGHTING); //Forge: Make sure that render states are reset, a renderEffect can derp them up.
|
||||
GL11.glEnable(GL11.GL_ALPHA_TEST);
|
||||
|
||||
ClientCacheHandler.getImageData(scriptCustomItem.getTexture()).bindTexture();
|
||||
renderCustomItemSlot(0, 0, 16, 16, itemRed, itemGreen, itemBlue);
|
||||
ImageData imageData = ClientCacheHandler.getImageData(scriptCustomItem.getTexture());
|
||||
imageData.bindTexture();
|
||||
|
||||
float vOff = 0f;
|
||||
float vEnd = 1f;
|
||||
if (imageData.isAnimated()) {
|
||||
vOff = imageData.getCurrentFrameVOffset();
|
||||
vEnd = vOff + (float) imageData.getFrameHeight() / imageData.getTotalHeight();
|
||||
} else if (scriptCustomItem.isTextureAnimated() != null && scriptCustomItem.isTextureAnimated()
|
||||
&& scriptCustomItem.getFrameCount() != null && scriptCustomItem.getFrameCount() > 1) {
|
||||
vOff = AnimationHelper.getFrameVOffset(imageData.getTotalHeight(), scriptCustomItem.getFrameCount(), scriptCustomItem.getFrameTime());
|
||||
vEnd = vOff + AnimationHelper.getFrameVSize(imageData.getTotalHeight(), scriptCustomItem.getFrameCount());
|
||||
}
|
||||
renderCustomItemSlot(0, 0, 16, 16, itemRed, itemGreen, itemBlue, vOff, vEnd);
|
||||
|
||||
GL11.glEnable(GL11.GL_LIGHTING);
|
||||
GL11.glDisable(GL11.GL_ALPHA_TEST);
|
||||
@@ -412,13 +441,17 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
}
|
||||
|
||||
public void renderCustomItemSlot(int posX, int posY, int imageWidth, int imageHeight, float itemRed, float itemGreen, float itemBlue) {
|
||||
renderCustomItemSlot(posX, posY, imageWidth, imageHeight, itemRed, itemGreen, itemBlue, 0f, 1f);
|
||||
}
|
||||
|
||||
public void renderCustomItemSlot(int posX, int posY, int imageWidth, int imageHeight, float itemRed, float itemGreen, float itemBlue, float vOff, float vEnd) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setColorRGBA_F(itemRed, itemGreen, itemBlue, 1.0F);
|
||||
tessellator.addVertexWithUV((double) (posX), (double) (posY + imageHeight), 0, 0, 1);
|
||||
tessellator.addVertexWithUV((double) (posX + imageWidth), (double) (posY + imageHeight), 0, 1, 1);
|
||||
tessellator.addVertexWithUV((double) (posX + imageWidth), (double) (posY), 0, 1, 0);
|
||||
tessellator.addVertexWithUV((double) (posX), (double) (posY), 0, 0, 0);
|
||||
tessellator.addVertexWithUV((double) (posX), (double) (posY + imageHeight), 0, 0, vEnd);
|
||||
tessellator.addVertexWithUV((double) (posX + imageWidth), (double) (posY + imageHeight), 0, 1, vEnd);
|
||||
tessellator.addVertexWithUV((double) (posX + imageWidth), (double) (posY), 0, 1, vOff);
|
||||
tessellator.addVertexWithUV((double) (posX), (double) (posY), 0, 0, vOff);
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
@@ -429,7 +462,8 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
TextureManager texturemanager = mc.getTextureManager();
|
||||
int par3 = 0;
|
||||
|
||||
ClientCacheHandler.getImageData(scriptCustomItem.getTexture()).bindTexture();
|
||||
ImageData item3dImageData = ClientCacheHandler.getImageData(scriptCustomItem.getTexture());
|
||||
item3dImageData.bindTexture();
|
||||
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
IIcon icon = entityLivingBase.getItemIcon(itemStack, par3);
|
||||
@@ -447,7 +481,19 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
GL11.glRotatef(335.0F, 0.0F, 0.0F, 1.0F);
|
||||
GL11.glTranslatef(-0.9375F, -0.0625F, 0.0F);
|
||||
|
||||
renderCustomItemIn2D(scriptCustomItem, tessellator, 1.0F, 0.0F, 0.0F, 1.0F, 0.0625F);
|
||||
// Animation V offset for 3D handheld rendering
|
||||
float item3dVOff = 0f;
|
||||
float item3dVEnd = 1f;
|
||||
if (item3dImageData.isAnimated()) {
|
||||
item3dVOff = item3dImageData.getCurrentFrameVOffset();
|
||||
item3dVEnd = item3dVOff + (float) item3dImageData.getFrameHeight() / item3dImageData.getTotalHeight();
|
||||
} else if (scriptCustomItem.isTextureAnimated() != null && scriptCustomItem.isTextureAnimated()
|
||||
&& scriptCustomItem.getFrameCount() != null && scriptCustomItem.getFrameCount() > 1) {
|
||||
item3dVOff = AnimationHelper.getFrameVOffset(item3dImageData.getTotalHeight(), scriptCustomItem.getFrameCount(), scriptCustomItem.getFrameTime());
|
||||
item3dVEnd = item3dVOff + AnimationHelper.getFrameVSize(item3dImageData.getTotalHeight(), scriptCustomItem.getFrameCount());
|
||||
}
|
||||
|
||||
renderCustomItemIn2D(scriptCustomItem, tessellator, 1.0F, item3dVOff, 0.0F, item3dVEnd, 0.0625F);
|
||||
|
||||
if (itemStack.hasEffect(par3)) {
|
||||
GL11.glDepthFunc(GL11.GL_EQUAL);
|
||||
@@ -485,7 +531,10 @@ public class ItemCustomRenderer implements IItemRenderer {
|
||||
public static void renderCustomItemIn2D(IItemCustomizable wrapper, Tessellator p_78439_0_, float p_78439_1_, float p_78439_2_, float p_78439_3_, float p_78439_4_, float p_78439_7_) {
|
||||
ImageData imageData = ClientCacheHandler.getImageData(wrapper.getTexture());
|
||||
int width = imageData.getTotalWidth();
|
||||
int height = imageData.getTotalHeight();
|
||||
// Use frame height for edge pixel calculations so animated strips render correctly
|
||||
int height = imageData.isAnimated() ? imageData.getFrameHeight()
|
||||
: (wrapper.isTextureAnimated() != null && wrapper.isTextureAnimated() && wrapper.getFrameCount() != null && wrapper.getFrameCount() > 1
|
||||
? imageData.getTotalHeight() / wrapper.getFrameCount() : imageData.getTotalHeight());
|
||||
|
||||
p_78439_0_.startDrawingQuads();
|
||||
p_78439_0_.setNormal(0.0F, 0.0F, 1.0F);
|
||||
|
||||
@@ -29,6 +29,11 @@ public class CustomEffect implements ICustomEffect {
|
||||
public String menuName = "§aNEW EFFECT";
|
||||
public int width = 16, height = 16;
|
||||
|
||||
// Animation
|
||||
public boolean animated = false;
|
||||
public int frameCount = 1;
|
||||
public int frametime = 2;
|
||||
|
||||
public int index = 0;
|
||||
|
||||
public CustomEffect() {
|
||||
@@ -137,6 +142,36 @@ public class CustomEffect implements ICustomEffect {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnimated() {
|
||||
return animated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnimated(boolean animated) {
|
||||
this.animated = animated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrameCount() {
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrameCount(int frameCount) {
|
||||
this.frameCount = Math.max(1, frameCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFrameTime() {
|
||||
return frametime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFrameTime(int frametime) {
|
||||
this.frametime = Math.max(1, frametime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLossOnDeath() {
|
||||
return lossOnDeath;
|
||||
@@ -222,6 +257,9 @@ public class CustomEffect implements ICustomEffect {
|
||||
compound.setInteger("iconHeight", height);
|
||||
compound.setString("icon", icon);
|
||||
compound.setBoolean("lossOnDeath", lossOnDeath);
|
||||
compound.setBoolean("iconAnimated", animated);
|
||||
compound.setInteger("iconFrameCount", frameCount);
|
||||
compound.setInteger("iconFrameTime", frametime);
|
||||
|
||||
if (saveScripts) {
|
||||
NBTTagCompound scriptData = new NBTTagCompound();
|
||||
@@ -264,6 +302,13 @@ public class CustomEffect implements ICustomEffect {
|
||||
icon = compound.getString("icon");
|
||||
lossOnDeath = compound.getBoolean("lossOnDeath");
|
||||
|
||||
if (compound.hasKey("iconAnimated"))
|
||||
animated = compound.getBoolean("iconAnimated");
|
||||
if (compound.hasKey("iconFrameCount"))
|
||||
frameCount = Math.max(1, compound.getInteger("iconFrameCount"));
|
||||
if (compound.hasKey("iconFrameTime"))
|
||||
frametime = Math.max(1, compound.getInteger("iconFrameTime"));
|
||||
|
||||
if (compound.hasKey("ScriptData", Constants.NBT.TAG_COMPOUND)) {
|
||||
EffectScript handler = new EffectScript();
|
||||
handler.readFromNBT(compound.getCompoundTag("ScriptData"));
|
||||
|
||||
@@ -12,6 +12,11 @@ public class ItemDisplayData {
|
||||
public Boolean durabilityShow = false;
|
||||
public Integer durabilityColor = -1;
|
||||
|
||||
// Animation
|
||||
public Boolean animated = false;
|
||||
public Integer frameCount = 1;
|
||||
public Integer frametime = 2;
|
||||
|
||||
public NBTTagCompound writeToNBT() {
|
||||
NBTTagCompound compound = new NBTTagCompound();
|
||||
this.writeToNBT(compound);
|
||||
@@ -42,6 +47,10 @@ public class ItemDisplayData {
|
||||
this.setFloat(compound, "TranslateY", this.translateY);
|
||||
this.setFloat(compound, "TranslateZ", this.translateZ);
|
||||
|
||||
this.setBoolean(compound, "Animated", this.animated);
|
||||
this.setInteger(compound, "FrameCount", this.frameCount);
|
||||
this.setInteger(compound, "FrameTime", this.frametime);
|
||||
|
||||
return compound;
|
||||
}
|
||||
|
||||
@@ -97,6 +106,16 @@ public class ItemDisplayData {
|
||||
if (compound.hasKey("TranslateZ")) {
|
||||
this.translateZ = compound.getFloat("TranslateZ");
|
||||
}
|
||||
|
||||
if (compound.hasKey("Animated")) {
|
||||
this.animated = compound.getBoolean("Animated");
|
||||
}
|
||||
if (compound.hasKey("FrameCount")) {
|
||||
this.frameCount = Math.max(1, compound.getInteger("FrameCount"));
|
||||
}
|
||||
if (compound.hasKey("FrameTime")) {
|
||||
this.frametime = Math.max(1, compound.getInteger("FrameTime"));
|
||||
}
|
||||
}
|
||||
|
||||
private void setBoolean(NBTTagCompound compound, String key, Boolean value) {
|
||||
|
||||
@@ -11,6 +11,11 @@ public class ScriptGuiTexturedRect extends ScriptGuiComponent implements ITextur
|
||||
float scale;
|
||||
String texture;
|
||||
|
||||
// Animation
|
||||
boolean animated = false;
|
||||
int frameCount = 1;
|
||||
int frametime = 2;
|
||||
|
||||
public ScriptGuiTexturedRect() {
|
||||
this.textureY = -1;
|
||||
this.scale = 1.0F;
|
||||
@@ -76,6 +81,25 @@ public class ScriptGuiTexturedRect extends ScriptGuiComponent implements ITextur
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isAnimated() {
|
||||
return this.animated && this.frameCount > 1;
|
||||
}
|
||||
|
||||
public int getFrameCount() {
|
||||
return this.frameCount;
|
||||
}
|
||||
|
||||
public int getFrameTime() {
|
||||
return this.frametime;
|
||||
}
|
||||
|
||||
public ITexturedRect setAnimation(int frameCount, int frametime) {
|
||||
this.animated = frameCount > 1;
|
||||
this.frameCount = Math.max(1, frameCount);
|
||||
this.frametime = Math.max(1, frametime);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return 2;
|
||||
}
|
||||
@@ -89,6 +113,12 @@ public class ScriptGuiTexturedRect extends ScriptGuiComponent implements ITextur
|
||||
nbt.setIntArray("texPos", new int[]{this.textureX, this.textureY});
|
||||
}
|
||||
|
||||
if (this.animated) {
|
||||
nbt.setBoolean("animated", true);
|
||||
nbt.setInteger("frameCount", this.frameCount);
|
||||
nbt.setInteger("frameTime", this.frametime);
|
||||
}
|
||||
|
||||
return nbt;
|
||||
}
|
||||
|
||||
@@ -101,6 +131,13 @@ public class ScriptGuiTexturedRect extends ScriptGuiComponent implements ITextur
|
||||
this.setTextureOffset(nbt.getIntArray("texPos")[0], nbt.getIntArray("texPos")[1]);
|
||||
}
|
||||
|
||||
if (nbt.hasKey("animated") && nbt.getBoolean("animated")) {
|
||||
this.setAnimation(
|
||||
nbt.hasKey("frameCount") ? Math.max(1, nbt.getInteger("frameCount")) : 1,
|
||||
nbt.hasKey("frameTime") ? Math.max(1, nbt.getInteger("frameTime")) : 2
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -147,6 +147,33 @@ public abstract class ScriptCustomizableItem extends ScriptItemStack implements
|
||||
return this.itemDisplay.translateZ != null ? this.itemDisplay.translateZ : 0;
|
||||
}
|
||||
|
||||
public Boolean isTextureAnimated() {
|
||||
return this.itemDisplay.animated != null ? this.itemDisplay.animated : false;
|
||||
}
|
||||
|
||||
public void setTextureAnimated(Boolean animated) {
|
||||
this.itemDisplay.animated = animated;
|
||||
saveItemData();
|
||||
}
|
||||
|
||||
public Integer getFrameCount() {
|
||||
return this.itemDisplay.frameCount != null ? this.itemDisplay.frameCount : 1;
|
||||
}
|
||||
|
||||
public void setFrameCount(Integer frameCount) {
|
||||
this.itemDisplay.frameCount = frameCount != null ? Math.max(1, frameCount) : 1;
|
||||
saveItemData();
|
||||
}
|
||||
|
||||
public Integer getFrameTime() {
|
||||
return this.itemDisplay.frametime != null ? this.itemDisplay.frametime : 2;
|
||||
}
|
||||
|
||||
public void setFrameTime(Integer frametime) {
|
||||
this.itemDisplay.frametime = frametime != null ? Math.max(1, frametime) : 2;
|
||||
saveItemData();
|
||||
}
|
||||
|
||||
public NBTTagCompound getMCNbt() {
|
||||
NBTTagCompound compound = super.getMCNbt();
|
||||
compound.setTag("ItemData", this.getItemNBT(new NBTTagCompound()));
|
||||
|
||||
Reference in new issue
Block a user