Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
* [ ] Pie menu

## High Priority

- [ ] merge animations with NEA
- [ ] check JEI click interactions
- [x] check JEI click interactions

## Medium Priority

Expand Down
2 changes: 2 additions & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ dependencies {
embed 'org.mariuszgromada.math:MathParser.org-mXparser:6.1.0'

implementation(rfg.deobf("curse.maven:baubles-227083:2518667"))
implementation rfg.deobf("curse.maven:neverenoughanimation-1062347:6533650-sources-6533651")
//implementation("com.cleanroommc:neverenoughanimations:1.0.6") { transitive false }
}
5 changes: 3 additions & 2 deletions src/main/java/com/cleanroommc/modularui/ClientProxy.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.cleanroommc.modularui;

import com.cleanroommc.modularui.animation.AnimatorManager;
import com.cleanroommc.modularui.drawable.DrawableSerialization;
import com.cleanroommc.modularui.factory.GuiFactories;
import com.cleanroommc.modularui.factory.inventory.InventoryTypes;
Expand Down Expand Up @@ -44,8 +45,8 @@ void preInit(FMLPreInitializationEvent event) {
super.preInit(event);

MinecraftForge.EVENT_BUS.register(ClientScreenHandler.class);
MinecraftForge.EVENT_BUS.register(OverlayManager.class);
MinecraftForge.EVENT_BUS.register(KeyBindHandler.class);
AnimatorManager.init();

if (ModularUIConfig.enableTestGuis) {
MinecraftForge.EVENT_BUS.register(EventHandler.class);
Expand Down Expand Up @@ -75,7 +76,7 @@ void postInit(FMLPostInitializationEvent event) {

@SubscribeEvent
public void onKeyboard(InputEvent.KeyInputEvent event) {
if (testKey.isPressed() && ModularUI.isBaubleLoaded()) {
if (ModularUIConfig.enableTestGuis && testKey.isPressed() && ModularUI.Mods.BAUBLES.isLoaded()) {
InventoryTypes.BAUBLES.visitAll(Minecraft.getMinecraft().player, (type, index, stack) -> {
if (stack.getItem() instanceof TestItem) {
GuiFactories.playerInventory().openFromBaublesClient(index);
Expand Down
65 changes: 44 additions & 21 deletions src/main/java/com/cleanroommc/modularui/ModularUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@

import net.minecraftforge.fml.common.Loader;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.ModContainer;
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import net.minecraftforge.fml.common.event.FMLServerStartingEvent;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import org.mariuszgromada.math.mxparser.License;

import java.util.function.Predicate;

@Mod(modid = ModularUI.ID,
name = ModularUI.NAME,
version = ModularUI.VERSION,
acceptedMinecraftVersions = "[1.12,)",
dependencies = "required-after:mixinbooter@[8.0,);" +
"after:bogorter@[1.4.0,);")
"after:bogorter@[1.4.0,);" +
"after-client:neverenoughanimations@[1.0.6,)")
public class ModularUI {

public static final String ID = MuiTags.MODID;
Expand All @@ -35,22 +40,13 @@ public class ModularUI {
@Mod.Instance
public static ModularUI INSTANCE;

private static boolean blurLoaded = false;
private static boolean sorterLoaded = false;
private static boolean jeiLoaded = false;
private static boolean baubleLoaded = false;

static {
// confirm mXparser license
License.iConfirmNonCommercialUse("CleanroomMC");
}

@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
blurLoaded = Loader.isModLoaded("blur");
sorterLoaded = Loader.isModLoaded(BOGO_SORT);
jeiLoaded = Loader.isModLoaded("jei");
baubleLoaded = Loader.isModLoaded("baubles");
proxy.preInit(event);
}

Expand All @@ -64,19 +60,46 @@ public void onServerLoad(FMLServerStartingEvent event) {
proxy.onServerLoad(event);
}

public static boolean isBlurLoaded() {
return blurLoaded;
public enum Mods {

BAUBLES(ModIds.BAUBLES),
BLUR(ModIds.BLUR),
BOGOSORTER(ModIds.BOGOSORTER),
JEI(ModIds.JEI),
NEA(ModIds.NEA);

public final String id;
private boolean loaded = false;
private boolean initialized = false;
private final Predicate<ModContainer> extraLoadedCheck;

Mods(String id) {
this(id, null);
}

Mods(String id, @Nullable Predicate<ModContainer> extraLoadedCheck) {
this.id = id;
this.extraLoadedCheck = extraLoadedCheck;
}

public boolean isLoaded() {
if (!this.initialized) {
this.loaded = Loader.isModLoaded(this.id);
if (this.loaded && this.extraLoadedCheck != null) {
this.loaded = this.extraLoadedCheck.test(Loader.instance().getIndexedModList().get(this.id));
}
this.initialized = true;
}
return this.loaded;
}
}

public static boolean isSortModLoaded() {
return sorterLoaded;
}

public static boolean isJeiLoaded() {
return jeiLoaded;
}
public static class ModIds {

public static boolean isBaubleLoaded() {
return baubleLoaded;
public static final String BLUR = "blur";
public static final String BOGOSORTER = "bogosorter";
public static final String JEI = "jei";
public static final String NEA = "neverenoughanimations";
public static final String BAUBLES = "baubles";
}
}
242 changes: 242 additions & 0 deletions src/main/java/com/cleanroommc/modularui/animation/Animator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
package com.cleanroommc.modularui.animation;

import com.cleanroommc.modularui.api.drawable.IInterpolation;
import com.cleanroommc.modularui.utils.Interpolation;

import java.util.function.DoubleConsumer;
import java.util.function.DoublePredicate;

public class Animator extends BaseAnimator implements IAnimator {

private float min = 0.0f;
private float max = 1.0f;
private int duration = 250;
private IInterpolation curve = Interpolation.LINEAR;
private boolean reverseOnFinish = false;
private int repeats = 0;
private DoublePredicate onUpdate;
private Runnable onFinish;

private int progress = 0;
private boolean startedReverse = false;
private int repeated = 0;

@Override
public void reset(boolean atEnd) {
this.progress = atEnd ? this.duration : 0;
this.startedReverse = atEnd;
this.repeated = 0;
}

@Override
public void stop(boolean force) {
if (isAnimating() && !force) {
if (this.reverseOnFinish && this.startedReverse == isAnimatingReverse()) {
onAnimationFinished(false, false);
// started reverse -> bounce back and animate forward
animate(isAnimatingForward());
return;
}
if (repeats != 0 && (repeated < repeats || repeats < 0)) {
onAnimationFinished(true, false);
// started forward -> full cycle finished -> try repeating
boolean reverse = !this.reverseOnFinish == isAnimatingReverse();
animate(reverse);
repeated++;
return;
}
}
super.stop(force);
}

@Override
public int advance(int elapsedTime) {
if (!isAnimating()) return elapsedTime;
while (elapsedTime > 0) {
int max = isAnimatingForward() ? this.duration - this.progress : this.progress;
int prog = Math.min(max, elapsedTime);
this.progress += prog * getDirection();
elapsedTime -= prog;
if (onUpdate()) {
stop(true);
break;
}
if ((isAnimatingForward() && this.progress >= this.duration) || (isAnimatingReverse() && this.progress <= 0)) {
stop(false);
if (!isAnimating()) {
onAnimationFinished(true, true);
break;
}
}
}
return elapsedTime;
}

protected boolean onUpdate() {
return this.onUpdate != null && this.onUpdate.test(getRawValue());
}

protected void onAnimationFinished(boolean finishedOneCycle, boolean finishedAllRepeats) {
if (this.onFinish != null) {
this.onFinish.run();
}
}

public boolean isAtEnd() {
return this.progress >= this.duration;
}

public boolean isAtStart() {
return this.progress <= 0;
}

public float getMin() {
return min;
}

public float getMax() {
return max;
}

public int getDuration() {
return duration;
}

public IInterpolation getCurve() {
return curve;
}

protected float getRawValue() {
return this.curve.interpolate(this.min, this.max, (float) this.progress / this.duration);
}

public float getValue() {
//advance();
return getRawValue();
}

@Override
public boolean hasProgressed() {
if (!isAnimating()) return false;
return isAnimatingForward() ? this.progress > 0 : this.progress < this.duration;
}

/**
* Sets the min bound of the value that will be interpolated.
*
* @param min min value
* @return this
*/
public Animator min(float min) {
this.min = min;
return this;
}

/**
* Sets the max bound of the value that will be interpolated.
*
* @param max max value
* @return this
*/
public Animator max(float max) {
this.max = max;
return this;
}

/**
* Sets the bounds of the value that will be interpolated.
*
* @param min min value
* @param max max value
* @return this
*/
public Animator bounds(float min, float max) {
this.min = min;
this.max = max;
return this;
}

/**
* The duration of this animation in milliseconds. Note this is not 100% accurate.
* Usually it's plus minus 2ms, but can rarely be more.
*
* @param duration duration in milliseconds
* @return this
*/
public Animator duration(int duration) {
this.duration = duration;
return this;
}

/**
* Sets the interpolation curve, which is used to interpolate between the bounds.
*
* @param curve curve to interpolate on
* @return this
*/
public Animator curve(IInterpolation curve) {
this.curve = curve;
return this;
}

/**
* Sets if the animation should reverse animate once after it finished.
* If the animation started in reverse it will animate forward on finish.
*
* @param reverseOnFinish if animation should bounce back on finish
* @return this
*/
public Animator reverseOnFinish(boolean reverseOnFinish) {
this.reverseOnFinish = reverseOnFinish;
return this;
}

/**
* Sets how often the animation should repeat. If {@link #reverseOnFinish(boolean)} is set to true, it will repeat the whole cycle.
* If the number of repeats is negative, it will repeat infinitely.
*
* @param repeats how often the animation should repeat.
* @return this
*/
public Animator repeatsOnFinish(int repeats) {
this.repeats = repeats;
return this;
}

/**
* Sets a function which is executed everytime the progress updates, that is on every frame.
* The argument of the function is the interpolated value.
*
* @param onUpdate update function
* @return this
*/
public Animator onUpdate(DoublePredicate onUpdate) {
this.onUpdate = onUpdate;
return this;
}

/**
* Sets a function which is executed everytime the progress updates, that is on every frame.
* The argument of the function is the interpolated value.
*
* @param onUpdate update function
* @return this
*/
public Animator onUpdate(DoubleConsumer onUpdate) {
return onUpdate(val -> {
onUpdate.accept(val);
return false;
});
}

/**
* Sets a function which is executed everytime, on animation, cycle or all repeats is finished.
*
* @param onFinish finish function
* @return this
*/
public Animator onFinish(Runnable onFinish) {
this.onFinish = onFinish;
return this;
}
}
Loading