Compare commits

...
25 Commits
Author SHA1 Message Date
selimaj-dev e8f686b2d3 Merge pull request 'Remove the upgrade of opaque theme colors from before 0.1.3' (#14) from drop-color-upgrade into master
Reviewed-on: #14
2026-09-30 19:49:03 +00:00
selimaj-devandclaude df5afe99e3 Remove the upgrade of opaque theme colors from before 0.1.3
Configs saved before 0.1.3 (without a menu blur) had their preset
background and primary colors made translucent on load. It's no longer
needed, so Theme drops it, along with ConfigManager.isSaved, which only
it used.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 21:47:45 +02:00
selimaj-dev 653738568a Merge pull request 'Rename the Ref methods that share a name with vanilla methods' (#13) from explicit-refs into master
Reviewed-on: #13
2026-09-30 16:03:45 +00:00
selimaj-devandclaude a6a324a61a Rename the Ref methods that share a name with vanilla methods
WindowRef.getWidth/getHeight and ItemStackRef.isEmpty/getMaxDamage
match methods of the classes they're mixed into, so the mixins can't
implement them explicitly. With Mojang's names, Window.getWidth is
also the framebuffer's width rather than the window's. They're now
getWindowWidth/getWindowHeight and isEmptyStack/getMaxItemDamage
(with getItemDamage to match).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-30 17:42:48 +02:00
selimaj-dev b0d75af8c4 Merge pull request 'Fix the None cloak and reorder the sidebar and title dock' (#12) from release-fixes into master
Reviewed-on: #12
2026-09-29 16:34:48 +00:00
selimaj-devandclaude c973e053aa Open Saturn's settings from the title dock and put Saturn first
The Saturn logo in the title screen's dock now opens Saturn's settings instead of the shift menu,
and it comes first: Saturn, mods, cosmetics, then Minecraft's options last. The shift menu is still
on its key in game.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 18:34:24 +02:00
selimaj-devandclaude bbf6b88ed3 Put settings second and the HUD editor last in the sidebar
The order is now mods, settings, cosmetics, HUD editor.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 18:34:24 +02:00
selimaj-devandclaude ba71a5b838 Draw no cloak for "None" instead of the missing texture
"None" is the empty cloak id, but getCurrentCloakTexture still built textures/cloaks/.png from it, so
the renderer drew Minecraft's missing texture. It now returns null for an empty id and for any id
this version has no texture for, and the renderer already skips a null texture.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 18:34:24 +02:00
selimaj-dev 6d5df3057e Merge pull request 'Add seven community emotes and re-export the twerk for emotes 0.1.1' (#11) from new-emotes into master
Reviewed-on: #11
2026-09-29 16:02:00 +00:00
selimaj-devandclaude 2fceb58024 Add seven community emotes and re-export the twerk for emotes 0.1.1
Get the Griddy, Take the L, Dab, Wave, Salute, Shrug and 67, converted from Emotecraft's binary
files with the emotes repo's tools/emotecraft/convert.py. Their authors stay in each file. Their
Emotecraft bends play as the library's curved ones.

Emotes 0.1.1 stopped flipping the torso's bend, so the twerk is re-exported with its torso bends'
signs turned round. It looks the same as before. This needs Saturn on emotes 0.1.1, which the
saturnclient change pointing here does.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 17:57:00 +02:00
selimaj-dev 7e98bbcd6c Merge pull request 'Add a twerk emote and drop the front flip' (#10) from twerk-emote into master
Reviewed-on: #10
2026-09-29 14:53:20 +00:00
selimaj-devandclaude 9ede612089 Add a twerk emote and drop the front flip
The twerk was measured from a 3D preview with the new emote capture tool, then made left/right
symmetric so it sways evenly instead of leaning to one side. The front flip is removed because it
plays wrongly with the new emote library.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-29 16:52:04 +02:00
selimaj-dev ed2914aaf7 Merge pull request 'Make old opaque theme colors translucent once' (#9) from upgrade-opaque-theme into master
Reviewed-on: #9
2026-09-28 21:10:28 +00:00
selimaj-devandclaude 69c830d466 Make old opaque theme colors translucent once
Configs saved before 0.1.3 hold the old default and preset background
and primary colors fully opaque, so glass didn't show for anyone
updating. On a config without Menu blur (new in 0.1.3), colors that are
an old default or preset get their translucent alpha; colors players
picked stay as they are.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 23:05:27 +02:00
selimaj-dev 7d0bfd26c9 Merge pull request 'Add GUI scale and glass, and rework the title screen' (#8) from glass-blur into master
Reviewed-on: #8
2026-09-28 20:01:19 +00:00
selimaj-devandclaude 7cc37b7c74 Tell when glass can be drawn at all
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:57:10 +02:00
selimaj-devandclaude 524fc36bf3 Say glass is up to the render scope, not the version
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:55:47 +02:00
selimaj-devandclaude 7bc308155e Split the blur into world, menu and widget blurs
The world or panorama keeps its animated background blur, while glass
behind menus and behind widgets (buttons, the dock, the account chip)
each get a fixed blur of their own. Glass is left out when its blur is 0.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 21:35:23 +02:00
selimaj-devandclaude 4d9c629eaf Frost translucent panels with glass
Panels whose background isn't solid draw the blurred game behind them,
cut to the same rounded rectangle texture as the background, where the
version supports glass. Panel screens then leave the rest of the game
unblurred.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 20:43:44 +02:00
selimaj-devandclaude 5c0912f0cf Use the background and primary colors' own alpha
Panels, buttons, cards and tiles multiplied the theme's background and
primary colors by a fixed alpha, so they were translucent however the
colors were set. They now use the colors as they are, and the defaults
and presets carry the old translucency in their alpha instead. Popups
stay nearly solid so they're readable over panels.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:56:24 +02:00
selimaj-devandclaude 3436054112 Draw the color picker from gradient textures
The picker drew its square and bars one pixel at a time, about 10,000
fills a frame. It now stretches four gradient strips, made once, over
them instead.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:45:04 +02:00
selimaj-devandclaude adedd688a7 Bring the title screen's intro and corners into line
The intro now comes in in reading order, one Main Menu stagger apart.
The account chip is as tall as the quit button, with a smaller head and
name, both sitting a little tighter in the top corners, and the Saturn
dock item opens the shift menu.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:39:39 +02:00
selimaj-devandclaude a7ad458627 Move the title screen's extras to its corners and dock
The account chip and a quit button go in the top corners and the version
in the bottom left, now smaller and fainter. Minecraft's options and
Saturn's settings take the HUD editor's place in the dock, and the intro
brings the menus and edges in after the logo. Faded elements now start
hidden instead of at 10% opacity.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:20:33 +02:00
selimaj-devandclaude 320b2fd9a5 Tighten the title screen around the logo
Give the title screen the shift menu's logo and wordmark, bring the
menus up under it, and make the dock smaller with a settings item.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 19:09:10 +02:00
selimaj-devandclaude a7e3e537b1 Add GUI scale sliders for Saturn's screens
The title screen, the menus and the HUD menus (emote wheel, shift menu)
each get a scale slider, relative to Minecraft's GUI scale. The HUD
editor stays in GUI pixels.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 18:59:47 +02:00
39 changed files with 4198 additions and 845 deletions
@@ -1,7 +1,11 @@
package org.saturnclient.common.ref.game; package org.saturnclient.common.ref.game;
/**
* An item stack. Its methods are implemented in ItemStackMixin, so their names must not match any of
* ItemStack's own methods.
*/
public interface ItemStackRef { public interface ItemStackRef {
public boolean isEmpty(); public boolean isEmptyStack();
public int getMaxDamage(); public int getMaxItemDamage();
public int getDamage(); public int getItemDamage();
} }
@@ -1,9 +1,15 @@
package org.saturnclient.common.ref.render; package org.saturnclient.common.ref.render;
/**
* The game window. Its methods are implemented in WindowMixin, so their names must not match any of
* Window's own methods.
*/
public interface WindowRef { public interface WindowRef {
public int getWidth(); /** The window's width in screen coordinates */
public int getWindowWidth();
public int getHeight(); /** The window's height in screen coordinates */
public int getWindowHeight();
public int getFramebufferWidth(); public int getFramebufferWidth();
@@ -6,6 +6,7 @@ import org.saturnclient.common.provider.GLFWProvider;
import org.saturnclient.common.provider.Providers; import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.config.property.BoolProperty; import org.saturnclient.config.property.BoolProperty;
import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.config.property.IntProperty; import org.saturnclient.config.property.IntProperty;
import org.saturnclient.config.property.KeybindingProperty; import org.saturnclient.config.property.KeybindingProperty;
import org.saturnclient.config.property.Property; import org.saturnclient.config.property.Property;
@@ -18,9 +19,33 @@ public class Config {
public static BoolProperty saturnTitleScreen = Property.bool(true); public static BoolProperty saturnTitleScreen = Property.bool(true);
public static BoolProperty cloakPhysics = Property.bool(true); public static BoolProperty cloakPhysics = Property.bool(true);
public static BoolProperty stagger = Property.bool(true); public static BoolProperty stagger = Property.bool(true);
/** The world or panorama behind Saturn's screens, faded in as they open */
public static IntProperty backgroundBlur = Property.integer(5).range(0, 10); public static IntProperty backgroundBlur = Property.integer(5).range(0, 10);
/** Glass behind translucent menus (panels), and widgets (buttons, the dock) */
public static IntProperty menuBlur = Property.integer(8).range(0, 20);
public static IntProperty widgetBlur = Property.integer(6).range(0, 20);
// How big Saturn's screens are, relative to Minecraft's GUI scale
public static FloatProperty titleScale = guiScale();
public static FloatProperty menuScale = guiScale();
public static FloatProperty hudMenuScale = guiScale();
/** Added in 0.1.3, so a config without it was saved by an older version */
static final String MENU_BLUR = "Menu blur";
public static KeybindingProperty openEmoteWheel = Property.keybinding(GLFWProvider.GLFW_KEY_B); public static KeybindingProperty openEmoteWheel = Property.keybinding(GLFWProvider.GLFW_KEY_B);
private static FloatProperty guiScale() {
return Property.floatProp(1f).range(0.5f, 2f, 0.05f).unit("×");
}
/**
* Whether any glass can be drawn: a glass blur is on, and a background it
* goes behind isn't solid.
*/
public static boolean usesGlass() {
boolean translucent = Theme.getPanelBg() >>> 24 < 0xFF || Theme.PRIMARY.value >>> 24 < 0xFF;
return translucent && (menuBlur.value > 0 || widgetBlur.value > 0);
}
public static IdentifierRef getLogo() { public static IdentifierRef getLogo() {
return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO; return realisticLogo.value ? Textures.REALISTIC_LOGO : Textures.LOGO;
} }
@@ -34,7 +59,12 @@ public class Config {
config.property("Open Emote Wheel", openEmoteWheel, "The key that opens the emote wheel"); config.property("Open Emote Wheel", openEmoteWheel, "The key that opens the emote wheel");
config.property("Cloak Physics", cloakPhysics, "Let cloaks swing as you move"); config.property("Cloak Physics", cloakPhysics, "Let cloaks swing as you move");
config.property("Stagger Animations", stagger, "Animate list items one after another"); config.property("Stagger Animations", stagger, "Animate list items one after another");
config.property("Background blur", backgroundBlur, "How much the game behind Saturn's screens is blurred"); config.property("World blur", backgroundBlur, "How much the world or panorama behind Saturn's screens is blurred");
config.property(MENU_BLUR, menuBlur, "How much the game is blurred through translucent menus");
config.property("Widget blur", widgetBlur, "How much the game is blurred through translucent buttons and the dock");
config.property("Title screen scale", titleScale, "How big the title screen is, relative to Minecraft's GUI scale");
config.property("Menu scale", menuScale, "How big the mods, settings and cosmetics menus are, relative to Minecraft's GUI scale");
config.property("HUD menu scale", hudMenuScale, "How big the emote wheel and shift menu are, relative to Minecraft's GUI scale");
// Initialize a sub namespace for theme // Initialize a sub namespace for theme
Theme.init(config); Theme.init(config);
@@ -156,10 +156,17 @@ public class ConfigManager {
* Load only a single property immediately after creation * Load only a single property immediately after creation
*/ */
private void loadSingleProperty(String name, Property prop) { private void loadSingleProperty(String name, Property prop) {
JsonNode element = getSaved(name);
if (element != null) {
prop.loadFromJson(element);
}
}
private JsonNode getSaved(String name) {
JsonNode source = cachedJson != null ? cachedJson : loadAndCache(); JsonNode source = cachedJson != null ? cachedJson : loadAndCache();
if (source == null || !source.isObject()) if (source == null || !source.isObject())
return; return null;
JsonNode currentScope = source; JsonNode currentScope = source;
@@ -170,16 +177,12 @@ public class ConfigManager {
if (next != null && next.isObject()) { if (next != null && next.isObject()) {
currentScope = next; currentScope = next;
} else { } else {
return; return null;
} }
} }
JsonNode element = currentScope.get(name); return currentScope.get(name);
if (element != null) {
prop.loadFromJson(element);
}
} }
/** /**
@@ -10,14 +10,14 @@ import org.saturnclient.config.property.SelectProperty;
public class Theme { public class Theme {
public static ConfigManager config; public static ConfigManager config;
// Main UI background (slightly warm dark to match Saturn tone) // Main UI background (slightly warm dark to match Saturn tone), translucent over the world
public static final ColorProperty BACKGROUND = Property.color(0xFF12100D); public static final ColorProperty BACKGROUND = Property.color(0xD112100D);
// Default text color // Default text color
public static final ColorProperty FOREGROUND = Property.color(0xFFF2E6D2); public static final ColorProperty FOREGROUND = Property.color(0xFFF2E6D2);
// Widget default background (normal state) // Widget default background (normal state), translucent over the world
public static final ColorProperty PRIMARY = Property.color(0xFF1C1915); public static final ColorProperty PRIMARY = Property.color(0xB81C1915);
// Widget text color // Widget text color
public static final ColorProperty PRIMARY_FG = Property.color(0xFFEAD9B8); public static final ColorProperty PRIMARY_FG = Property.color(0xFFEAD9B8);
@@ -62,10 +62,10 @@ public class Theme {
} }
public static final List<Preset> PRESETS = List.of( public static final List<Preset> PRESETS = List.of(
new Preset("Saturn", 0xFF12100D, 0xFFF2E6D2, 0xFF1C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D), new Preset("Saturn", 0xD112100D, 0xFFF2E6D2, 0xB81C1915, 0xFFEAD9B8, 0xFFE2BF7D, 0xFF1A140A, 0xFFE2BF7D),
new Preset("Midnight", 0xFF0E1117, 0xFFE4EAF5, 0xFF171C26, 0xFFC9D4E8, 0xFF8FB3FF, 0xFF0A1224, 0xFF8FB3FF), new Preset("Midnight", 0xD10E1117, 0xFFE4EAF5, 0xB8171C26, 0xFFC9D4E8, 0xFF8FB3FF, 0xFF0A1224, 0xFF8FB3FF),
new Preset("Amethyst", 0xFF120F17, 0xFFEDE4F7, 0xFF1C1724, 0xFFD8C9EB, 0xFFC79BFF, 0xFF1A0F2A, 0xFFC79BFF), new Preset("Amethyst", 0xD1120F17, 0xFFEDE4F7, 0xB81C1724, 0xFFD8C9EB, 0xFFC79BFF, 0xFF1A0F2A, 0xFFC79BFF),
new Preset("Emerald", 0xFF0D1210, 0xFFE2F2EA, 0xFF16201B, 0xFFC4E0D2, 0xFF7FD1A8, 0xFF0A1F15, 0xFF7FD1A8)); new Preset("Emerald", 0xD10D1210, 0xFFE2F2EA, 0xB816201B, 0xFFC4E0D2, 0xFF7FD1A8, 0xFF0A1F15, 0xFF7FD1A8));
public static void init(ConfigManager parent) { public static void init(ConfigManager parent) {
config = new ConfigManager(parent, "UI Style"); config = new ConfigManager(parent, "UI Style");
@@ -98,10 +98,19 @@ public class Theme {
} }
/** /**
* Translucent background for panels drawn over the world * Background for panels drawn over the world, as translucent as the
* background color is
*/ */
public static int getPanelBg() { public static int getPanelBg() {
return withAlpha(0.82f, BACKGROUND.value); return BACKGROUND.value;
}
/**
* Background for popups, which sit over other panels: the background color,
* kept nearly solid so what's under it doesn't show through
*/
public static int getPopupBg() {
return withAlpha(0.97f, BACKGROUND.value | 0xFF000000);
} }
/** /**
@@ -38,7 +38,14 @@ public class Cloaks {
availableCloaks.add(0, ""); availableCloaks.add(0, "");
} }
/**
* The cloak's texture, or null for no cloak ("", the "None" choice) and for a cloak this version
* has no texture for, which would otherwise draw as the missing texture.
*/
public static IdentifierRef getCurrentCloakTexture(String cloakName) { public static IdentifierRef getCurrentCloakTexture(String cloakName) {
if (cloakName == null || !Arrays.asList(ALL_CLOAKS).contains(cloakName)) {
return null;
}
if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) { if (Arrays.asList(ANIMATED_CLOAKS).contains(cloakName)) {
return IdentifierRef.ofSaturn("textures/cloaks/" + cloakName + ".gif"); return IdentifierRef.ofSaturn("textures/cloaks/" + cloakName + ".gif");
} else { } else {
@@ -17,7 +17,7 @@ import org.saturnclient.ui.Utils;
*/ */
public class Emotes { public class Emotes {
private static final List<String> IDS = List.of("tpose", "back_flip", "facepalm", "cry", "giga_chad", private static final List<String> IDS = List.of("tpose", "back_flip", "facepalm", "cry", "giga_chad",
"front_flip"); "twerk", "get_the_griddy", "take_the_l", "dab", "wave", "salute", "shrug", "67");
private static final EmoteRegistry REGISTRY = new EmoteRegistry(); private static final EmoteRegistry REGISTRY = new EmoteRegistry();
@@ -75,16 +75,16 @@ public class ArmorDisplayMod extends Mod implements HudMod {
int row = 0; int row = 0;
if (useMainHand.value && !mainHand.isEmpty()) { if (useMainHand.value && !mainHand.isEmptyStack()) {
row = renderSlot(scope, mainHand, row); row = renderSlot(scope, mainHand, row);
} }
if (!helmet.isEmpty()) if (!helmet.isEmptyStack())
row = renderSlot(scope, helmet, row); row = renderSlot(scope, helmet, row);
if (!chestplate.isEmpty()) if (!chestplate.isEmptyStack())
row = renderSlot(scope, chestplate, row); row = renderSlot(scope, chestplate, row);
if (!leggings.isEmpty()) if (!leggings.isEmptyStack())
row = renderSlot(scope, leggings, row); row = renderSlot(scope, leggings, row);
if (!boots.isEmpty()) if (!boots.isEmptyStack())
row = renderSlot(scope, boots, row); row = renderSlot(scope, boots, row);
} }
@@ -94,8 +94,8 @@ public class ArmorDisplayMod extends Mod implements HudMod {
private int renderSlot(RenderScope scope, ItemStackRef item, int row) { private int renderSlot(RenderScope scope, ItemStackRef item, int row) {
scope.drawItem(item, 0, 15 * row); scope.drawItem(item, 0, 15 * row);
if (item.getMaxDamage() > 0) { if (item.getMaxItemDamage() > 0) {
int remaining = item.getMaxDamage() - item.getDamage(); int remaining = item.getMaxItemDamage() - item.getItemDamage();
scope.drawText(0.5f, String.valueOf(remaining), scope.drawText(0.5f, String.valueOf(remaining),
17, (15 * row) + 3, 17, (15 * row) + 3,
layout.font.value, layout.fgColor.value); layout.font.value, layout.fgColor.value);
@@ -26,6 +26,11 @@ public class ElementRenderer {
for (Element element : elements) { for (Element element : elements) {
element.playAnimationFrame(elapsed); element.playAnimationFrame(elapsed);
// Not faded in yet
if (element.opacity <= 0) {
continue;
}
renderScope.getMatrixStack().push(); renderScope.getMatrixStack().push();
renderScope.setOpacity(element.opacity); renderScope.setOpacity(element.opacity);
renderScope.getMatrixStack().translate(element.x, element.y); renderScope.getMatrixStack().translate(element.x, element.y);
@@ -34,6 +34,14 @@ public interface RenderScope {
if (color == 0) if (color == 0)
return; return;
drawTexture(getRoundedRectangle(width, height, radius), x, y, 0, 0, width, height, color);
}
/**
* The white rounded rectangle that {@link #drawRoundedRectangle} tints, as a
* cached texture.
*/
default IdentifierRef getRoundedRectangle(int width, int height, int radius) {
radius = Math.min(radius, width / 2); radius = Math.min(radius, width / 2);
radius = Math.min(radius, height / 2); radius = Math.min(radius, height / 2);
@@ -42,7 +50,38 @@ public interface RenderScope {
width, height, width, height,
radius, radius); radius, radius);
drawSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, x, y, width, height, color); return getSvgShape(rect, "rounded_" + width + "_" + height + "_" + radius, width, height);
}
/**
* Frosts a rounded rectangle for a translucent background to be drawn over:
* the game behind the screen, blurred by {@code blur}, cut to exactly the
* shape {@link #drawRoundedRectangle} draws. Render scopes without glass
* draw nothing.
*/
default void drawGlass(int x, int y, int width, int height, int radius, int blur) {
drawGlass(getRoundedRectangle(width, height, radius), x, y, width, height, blur);
}
/**
* Draws the game behind the screen, blurred by {@code blur}, where
* {@code mask}, stretched over the area, is opaque.
*/
default void drawGlass(IdentifierRef mask, int x, int y, int width, int height, int blur) {
}
/**
* A {@link #drawRoundedRectangle} with a border, frosted with glass blurred
* by {@code blur} when the background isn't solid. A blur of 0 leaves it
* plain, showing the screen's own background blur.
*/
default void drawGlassRectangle(int x, int y, int width, int height, int radius, int blur, int thickness,
int background, int border) {
if (blur > 0 && background >>> 24 < 0xFF) {
drawGlass(x, y, width, height, radius, blur);
}
drawRoundedRectangle(x, y, width, height, radius, thickness, background, border);
} }
/** /**
@@ -89,15 +128,22 @@ public interface RenderScope {
* {@code color}. * {@code color}.
*/ */
private void drawSvgShape(String shape, String cacheKey, int x, int y, int width, int height, int color) { private void drawSvgShape(String shape, String cacheKey, int x, int y, int width, int height, int color) {
drawTexture(getSvgShape(shape, cacheKey, width, height), x, y, 0, 0, width, height, color);
}
/**
* {@code shape} (SVG elements in a {@code width} x {@code height} canvas,
* drawn in white) rendered to a cached texture.
*/
private IdentifierRef getSvgShape(String shape, String cacheKey, int width, int height) {
String svg = String.format( String svg = String.format(
"<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>%s</svg>", "<svg xmlns='http://www.w3.org/2000/svg' width='%d' height='%d'>%s</svg>",
width, height, shape); width, height, shape);
InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8)); InputStream svgStream = new ByteArrayInputStream(svg.getBytes(StandardCharsets.UTF_8));
drawTexture(SvgTexture.getSvg(svgStream, return SvgTexture.getSvg(svgStream, IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey),
IdentifierRef.ofSaturn("textures/gui/tmp_rect/" + cacheKey), width * 4, height * 4);
width * 4, height * 4), x, y, 0, 0, width, height, color);
} }
void drawBorder(int x, int y, int width, int height, int color); void drawBorder(int x, int y, int width, int height, int color);
@@ -6,6 +6,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.property.FloatProperty;
public abstract class SaturnScreen { public abstract class SaturnScreen {
/** How long the background blur takes to fade in, in ms */ /** How long the background blur takes to fade in, in ms */
@@ -14,8 +15,10 @@ public abstract class SaturnScreen {
public static interface ScreenProvider { public static interface ScreenProvider {
public void close(); public void close();
/** The screen's width in GUI pixels */
public int getWidth(); public int getWidth();
/** The screen's height in GUI pixels */
public int getHeight(); public int getHeight();
} }
@@ -23,6 +26,10 @@ public abstract class SaturnScreen {
public int width; public int width;
public int height; public int height;
public String title; public String title;
/** GUI pixels per Saturn unit, from {@link #getScale()} when the screen was built */
public float scale = 0.5f;
/** Whether a mouse button is held, so the screen isn't rebuilt under a drag */
private boolean mouseDown = false;
// Saturn Screen // Saturn Screen
public List<Element> elements = new ArrayList<>(); public List<Element> elements = new ArrayList<>();
@@ -51,10 +58,39 @@ public abstract class SaturnScreen {
return Math.round(Config.backgroundBlur.value * Math.min(elapsed / (float) BLUR_FADE_IN, 1f)); return Math.round(Config.backgroundBlur.value * Math.min(elapsed / (float) BLUR_FADE_IN, 1f));
} }
/**
* The GUI scale setting this screen follows; the menu scale unless the
* screen is another kind.
*/
protected FloatProperty getScaleSetting() {
return Config.menuScale;
}
/**
* GUI pixels per Saturn unit. At 1× a Saturn unit is half a GUI pixel.
* Enlarging stops where the screen would be smaller than 320x240 GUI
* pixels, the least Minecraft's own GUI scale leaves.
*/
public float getScale() {
float fit = Math.min(provider.getWidth() / 320f, provider.getHeight() / 240f);
return 0.5f * Math.min(getScaleSetting().value, Math.max(1f, fit));
}
/**
* Rebuilds the screen if its scale setting changed, once no drag is going
* on (the slider being dragged would be replaced).
*/
public void updateScale() {
if (!mouseDown && getScale() != scale) {
init();
}
}
public void init() { public void init() {
elements.clear(); elements.clear();
width = provider.getWidth(); scale = getScale();
height = provider.getHeight(); width = (int) (provider.getWidth() / scale);
height = (int) (provider.getHeight() / scale);
Popup.reset(width, height); Popup.reset(width, height);
ui(); // abstraction to render the saturn ui and also render extra stuff here ui(); // abstraction to render the saturn ui and also render extra stuff here
} }
@@ -72,7 +108,7 @@ public abstract class SaturnScreen {
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) { public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
renderScope.getMatrixStack().push(); renderScope.getMatrixStack().push();
renderScope.getMatrixStack().scale(0.5f, 0.5f); renderScope.getMatrixStack().scale(scale, scale);
ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY); ElementRenderer.render(new ArrayList<>(elements), elapsed, renderScope, mouseX, mouseY);
Popup.render(renderScope, elapsed, mouseX, mouseY); Popup.render(renderScope, elapsed, mouseX, mouseY);
@@ -85,8 +121,9 @@ public abstract class SaturnScreen {
// -------------- // --------------
public boolean mouseClicked(double mouseX, double mouseY, int button) { public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX *= 2; mouseDown = true;
mouseY *= 2; mouseX /= scale;
mouseY /= scale;
if (!Popup.mouseClicked(mouseX, mouseY, button)) { if (!Popup.mouseClicked(mouseX, mouseY, button)) {
ElementRenderer.mouseClicked(elements, mouseX, mouseY, button); ElementRenderer.mouseClicked(elements, mouseX, mouseY, button);
@@ -96,8 +133,8 @@ public abstract class SaturnScreen {
} }
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
mouseX *= 2; mouseX /= scale;
mouseY *= 2; mouseY /= scale;
if (!Popup.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) { if (!Popup.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)) {
ElementRenderer.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY); ElementRenderer.mouseDragged(elements, mouseX, mouseY, button, deltaX, deltaY);
@@ -107,8 +144,9 @@ public abstract class SaturnScreen {
} }
public boolean mouseReleased(double mouseX, double mouseY, int button) { public boolean mouseReleased(double mouseX, double mouseY, int button) {
mouseX *= 2; mouseDown = false;
mouseY *= 2; mouseX /= scale;
mouseY /= scale;
// Everything hears the release, so no element is left thinking it's being dragged. // Everything hears the release, so no element is left thinking it's being dragged.
Popup.mouseReleased(mouseX, mouseY, button); Popup.mouseReleased(mouseX, mouseY, button);
@@ -122,8 +160,8 @@ public abstract class SaturnScreen {
double mouseY, double mouseY,
double horizontalAmount, double horizontalAmount,
double verticalAmount) { double verticalAmount) {
mouseX *= 2; mouseX /= scale;
mouseY *= 2; mouseY /= scale;
if (!Popup.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)) { if (!Popup.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount)) {
ElementRenderer.mouseScrolled(elements, mouseX, mouseY, horizontalAmount, verticalAmount); ElementRenderer.mouseScrolled(elements, mouseX, mouseY, horizontalAmount, verticalAmount);
@@ -16,12 +16,12 @@ public class Fade extends Animation {
@Override @Override
public void tick(double progress, Element element) { public void tick(double progress, Element element) {
element.opacity = Math.max(0.1f, targetOpacity * (float) progress); element.opacity = targetOpacity * (float) progress;
} }
@Override @Override
public void init(Element element) { public void init(Element element) {
targetOpacity = element.opacity; targetOpacity = element.opacity;
element.opacity = 0.1f; element.opacity = 0;
} }
} }
@@ -3,6 +3,7 @@ package org.saturnclient.ui.components;
import org.saturnclient.client.ServiceClient; import org.saturnclient.client.ServiceClient;
import org.saturnclient.common.provider.Providers; import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.ElementContext;
@@ -15,12 +16,13 @@ import org.saturnclient.ui.resources.Fonts;
* while connected to Saturn. * while connected to Saturn.
*/ */
public class AccountChip extends Element { public class AccountChip extends Element {
public static final int HEIGHT = 40; public static final int HEIGHT = 32;
private static final int HEAD = 28; private static final int HEAD = 18;
private static final int PADDING = 6; private static final int PADDING = 7;
private static final int DOT = 7; private static final int GAP = 8;
private static final float NAME_SCALE = 0.7f; private static final int DOT = 6;
private static final float NAME_SCALE = 0.55f;
private static final int CONNECTED = 0xFF7FD1A8; private static final int CONNECTED = 0xFF7FD1A8;
private final String name; private final String name;
@@ -29,13 +31,14 @@ public class AccountChip extends Element {
String username = Providers.saturn.getClient().getUsername(); String username = Providers.saturn.getClient().getUsername();
this.name = username != null ? username : "Player"; this.name = username != null ? username : "Player";
this.height = HEIGHT; this.height = HEIGHT;
this.width = PADDING + HEAD + 10 + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + 10 + DOT + 12; this.width = PADDING + HEAD + GAP + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + GAP + DOT
+ GAP + 2;
} }
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, Theme.getPanelBg(), renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value, 1,
Theme.getBorder()); Theme.getPanelBg(), Theme.getBorder());
// The face, then the hat layer over it, from the 64x64 skin // The face, then the hat layer over it, from the 64x64 skin
IdentifierRef skin = Providers.saturn.getPlayerSkin(); IdentifierRef skin = Providers.saturn.getPlayerSkin();
@@ -43,11 +46,11 @@ public class AccountChip extends Element {
renderScope.drawTexture(skin, PADDING, headY, 8, 8, HEAD, HEAD, 8, 8, 64, 64); renderScope.drawTexture(skin, PADDING, headY, 8, 8, HEAD, HEAD, 8, 8, 64, 64);
renderScope.drawTexture(skin, PADDING, headY, 40, 8, HEAD, HEAD, 8, 8, 64, 64); renderScope.drawTexture(skin, PADDING, headY, 40, 8, HEAD, HEAD, 8, 8, 64, 64);
int nameX = PADDING + HEAD + 10; int nameX = PADDING + HEAD + GAP;
renderScope.drawText(NAME_SCALE, name, nameX, Field.textY(height, NAME_SCALE), Theme.FONT.value, renderScope.drawText(NAME_SCALE, name, nameX, Field.textY(height, NAME_SCALE), Theme.FONT.value,
Theme.FOREGROUND.value); Theme.FOREGROUND.value);
int dotX = nameX + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + 10; int dotX = nameX + (int) (Fonts.getWidth(name, Theme.FONT.value) * NAME_SCALE) + GAP;
renderScope.drawRoundedRectangle(dotX, (height - DOT) / 2, DOT, DOT, DOT / 2, renderScope.drawRoundedRectangle(dotX, (height - DOT) / 2, DOT, DOT, DOT / 2,
ServiceClient.isConnected() ? CONNECTED : Theme.withAlpha(0.3f, Theme.FOREGROUND.value)); ServiceClient.isConnected() ? CONNECTED : Theme.withAlpha(0.3f, Theme.FOREGROUND.value));
} }
@@ -60,7 +60,7 @@ public class CosmeticTile extends Element {
int border = isEquipped ? Theme.ACCENT.value int border = isEquipped ? Theme.ACCENT.value
: Theme.withAlpha(hovered ? 0.22f : 0.06f, Theme.FOREGROUND.value); : Theme.withAlpha(hovered ? 0.22f : 0.06f, Theme.FOREGROUND.value);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, isEquipped ? 2 : 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, isEquipped ? 2 : 1,
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border); Theme.PRIMARY.value, border);
// Preview, fitted into its area // Preview, fitted into its area
int areaWidth = width - INSET * 2; int areaWidth = width - INSET * 2;
@@ -3,6 +3,7 @@ package org.saturnclient.ui.components;
import java.util.List; import java.util.List;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.ElementContext;
@@ -15,12 +16,12 @@ public class Dock extends Element {
public record Item(IdentifierRef icon, Runnable onClick) { public record Item(IdentifierRef icon, Runnable onClick) {
} }
public static final int HEIGHT = 52; public static final int HEIGHT = 40;
private static final int PADDING = 6; private static final int PADDING = 5;
private static final int ITEM = HEIGHT - PADDING * 2; private static final int ITEM = HEIGHT - PADDING * 2;
private static final int ICON = 18; private static final int ICON = 15;
private static final int GAP = 4; private static final int GAP = 3;
private final List<Item> items; private final List<Item> items;
@@ -32,7 +33,8 @@ public class Dock extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value + 4, 1, Theme.getPanelBg(), renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value + 4, Config.widgetBlur.value, 1,
Theme.getPanelBg(),
Theme.withAlpha(0.1f, Theme.FOREGROUND.value)); Theme.withAlpha(0.1f, Theme.FOREGROUND.value));
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {
@@ -41,7 +41,7 @@ public class EmoteTile extends Element {
boolean hovered = ctx.isHovering(); boolean hovered = ctx.isHovering();
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, hovered ? 2 : 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, hovered ? 2 : 1,
Theme.withAlpha(0.72f, Theme.PRIMARY.value), Theme.PRIMARY.value,
hovered ? Theme.ACCENT.value : Theme.withAlpha(0.06f, Theme.FOREGROUND.value)); hovered ? Theme.ACCENT.value : Theme.withAlpha(0.06f, Theme.FOREGROUND.value));
int imageSize = height - INSET * 2 - NAME_AREA; int imageSize = height - INSET * 2 - NAME_AREA;
@@ -46,7 +46,7 @@ public class SaturnModule extends Element {
// Card // Card
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border); Theme.PRIMARY.value, border);
if (enabled) { if (enabled) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value,
@@ -22,9 +22,9 @@ public class Sidebar extends Element {
// Values for the constructor's `active` // Values for the constructor's `active`
public static final int MODS = 0; public static final int MODS = 0;
public static final int COSMETICS = 1; public static final int SETTINGS = 1;
public static final int HUD_EDITOR = 2; public static final int COSMETICS = 2;
public static final int SETTINGS = 3; public static final int HUD_EDITOR = 3;
private static final int LOGO = 32; private static final int LOGO = 32;
private static final int BUTTON = 44; private static final int BUTTON = 44;
@@ -35,11 +35,12 @@ public class Sidebar extends Element {
private record Item(IdentifierRef icon, Runnable onClick) { private record Item(IdentifierRef icon, Runnable onClick) {
} }
// In the order of the constants above
private final Item[] items = { private final Item[] items = {
new Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())), new Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())),
new Item(Textures.ICON_SETTINGS, () -> Providers.saturn.getClient().setScreen(new ClientSettings())),
new Item(Textures.ICON_COSMETICS, () -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())), new Item(Textures.ICON_COSMETICS, () -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())),
new Item(Textures.ICON_HUD_EDITOR, () -> Providers.saturn.getClient().setScreen(new HudEditor())), new Item(Textures.ICON_HUD_EDITOR, () -> Providers.saturn.getClient().setScreen(new HudEditor())),
new Item(Textures.ICON_SETTINGS, () -> Providers.saturn.getClient().setScreen(new ClientSettings())),
}; };
private final int active; private final int active;
@@ -38,7 +38,7 @@ public class ThemePresetCard extends Element {
: Theme.withAlpha(ctx.isHovering() ? 0.22f : 0.06f, Theme.FOREGROUND.value); : Theme.withAlpha(ctx.isHovering() ? 0.22f : 0.06f, Theme.FOREGROUND.value);
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, active ? 2 : 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, active ? 2 : 1,
Theme.withAlpha(0.72f, Theme.PRIMARY.value), border); Theme.PRIMARY.value, border);
// A panel in the preset's colors: a line of text over an accent and a widget // A panel in the preset's colors: a line of text over an accent and a widget
int previewWidth = width - PADDING * 2; int previewWidth = width - PADDING * 2;
@@ -1,7 +1,10 @@
package org.saturnclient.ui.components.inputs; package org.saturnclient.ui.components.inputs;
import java.awt.Color; import java.awt.Color;
import java.awt.image.BufferedImage;
import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.config.property.ColorProperty; import org.saturnclient.config.property.ColorProperty;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
@@ -27,6 +30,18 @@ public class ColorPicker extends Element {
private static final int SQUARE_Y = OPACITY_Y + SLIDER_HEIGHT + GAP; private static final int SQUARE_Y = OPACITY_Y + SLIDER_HEIGHT + GAP;
private static final int HUE_Y = SQUARE_Y + SIZE + GAP; private static final int HUE_Y = SQUARE_Y + SIZE + GAP;
// Gradient strips, made once and stretched over each part, so a frame is a
// few draws instead of one per pixel. Tinting the white ones colors them.
private static final int STRIP = 256;
/** White fading out to the right: saturation, over the hue */
private static final IdentifierRef SATURATION = IdentifierRef.ofSaturn("textures/gui/color_picker/saturation");
/** Black fading in downwards: brightness, over the saturation */
private static final IdentifierRef BRIGHTNESS = IdentifierRef.ofSaturn("textures/gui/color_picker/brightness");
/** White fading in to the right, tinted with the color */
private static final IdentifierRef OPACITY = IdentifierRef.ofSaturn("textures/gui/color_picker/opacity");
private static final IdentifierRef HUE = IdentifierRef.ofSaturn("textures/gui/color_picker/hue");
private static boolean stripsRegistered = false;
private final ColorProperty prop; private final ColorProperty prop;
private float hue; private float hue;
@@ -46,8 +61,10 @@ public class ColorPicker extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
registerStrips();
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value)); Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
renderOpacity(renderScope); renderOpacity(renderScope);
renderSquare(renderScope); renderSquare(renderScope);
@@ -55,22 +72,16 @@ public class ColorPicker extends Element {
} }
private void renderOpacity(RenderScope renderScope) { private void renderOpacity(RenderScope renderScope) {
int rgb = prop.value & 0xFFFFFF; renderScope.drawTexture(OPACITY, PADDING, OPACITY_Y, 0, 0, SIZE, SLIDER_HEIGHT,
for (int x = 0; x < SIZE; x++) { 0xFF000000 | prop.value);
int alpha = (int) (x / (float) SIZE * 255) & 0xFF;
renderScope.fill(PADDING + x, OPACITY_Y, PADDING + x + 1, OPACITY_Y + SLIDER_HEIGHT, (alpha << 24) | rgb);
}
drawMarker(renderScope, PADDING + (int) (opacity * SIZE), OPACITY_Y); drawMarker(renderScope, PADDING + (int) (opacity * SIZE), OPACITY_Y);
} }
private void renderSquare(RenderScope renderScope) { private void renderSquare(RenderScope renderScope) {
for (int x = 0; x < SIZE; x++) { renderScope.fill(PADDING, SQUARE_Y, PADDING + SIZE, SQUARE_Y + SIZE, 0xFF000000 | Color.HSBtoRGB(hue, 1f, 1f));
for (int y = 0; y < SIZE; y++) { renderScope.drawTexture(SATURATION, PADDING, SQUARE_Y, 0, 0, SIZE, SIZE, 0xFFFFFFFF);
int color = Color.HSBtoRGB(hue, x / (float) SIZE, 1f - y / (float) SIZE); renderScope.drawTexture(BRIGHTNESS, PADDING, SQUARE_Y, 0, 0, SIZE, SIZE, 0xFFFFFFFF);
renderScope.fill(PADDING + x, SQUARE_Y + y, PADDING + x + 1, SQUARE_Y + y + 1, 0xFF000000 | color);
}
}
int selX = PADDING + (int) (saturation * SIZE) - 4; int selX = PADDING + (int) (saturation * SIZE) - 4;
int selY = SQUARE_Y + (int) ((1f - brightness) * SIZE) - 4; int selY = SQUARE_Y + (int) ((1f - brightness) * SIZE) - 4;
@@ -79,14 +90,36 @@ public class ColorPicker extends Element {
} }
private void renderHue(RenderScope renderScope) { private void renderHue(RenderScope renderScope) {
for (int x = 0; x < SIZE; x++) { renderScope.drawTexture(HUE, PADDING, HUE_Y, 0, 0, SIZE, SLIDER_HEIGHT, 0xFFFFFFFF);
int color = Color.HSBtoRGB(x / (float) SIZE, 1f, 1f);
renderScope.fill(PADDING + x, HUE_Y, PADDING + x + 1, HUE_Y + SLIDER_HEIGHT, 0xFF000000 | color);
}
drawMarker(renderScope, PADDING + (int) (hue * SIZE), HUE_Y); drawMarker(renderScope, PADDING + (int) (hue * SIZE), HUE_Y);
} }
private static void registerStrips() {
if (stripsRegistered) {
return;
}
BufferedImage saturation = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
BufferedImage brightness = new BufferedImage(1, STRIP, BufferedImage.TYPE_INT_ARGB);
BufferedImage opacity = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
BufferedImage hue = new BufferedImage(STRIP, 1, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i < STRIP; i++) {
int alpha = i * 255 / (STRIP - 1);
saturation.setRGB(i, 0, ((255 - alpha) << 24) | 0xFFFFFF);
brightness.setRGB(0, i, alpha << 24);
opacity.setRGB(i, 0, (alpha << 24) | 0xFFFFFF);
hue.setRGB(i, 0, 0xFF000000 | Color.HSBtoRGB(i / (float) STRIP, 1f, 1f));
}
Providers.saturn.registerBufferedImageTexture(SATURATION, saturation);
Providers.saturn.registerBufferedImageTexture(BRIGHTNESS, brightness);
Providers.saturn.registerBufferedImageTexture(OPACITY, opacity);
Providers.saturn.registerBufferedImageTexture(HUE, hue);
stripsRegistered = true;
}
private static void drawMarker(RenderScope renderScope, int x, int sliderY) { private static void drawMarker(RenderScope renderScope, int x, int sliderY) {
renderScope.fill(x - 2, sliderY - 2, x + 2, sliderY + SLIDER_HEIGHT + 2, 0xFFFFFFFF); renderScope.fill(x - 2, sliderY - 2, x + 2, sliderY + SLIDER_HEIGHT + 2, 0xFFFFFFFF);
renderScope.fill(x - 1, sliderY - 1, x + 1, sliderY + SLIDER_HEIGHT + 1, 0xFF000000); renderScope.fill(x - 1, sliderY - 1, x + 1, sliderY + SLIDER_HEIGHT + 1, 0xFF000000);
@@ -38,7 +38,7 @@ public class CurvePreview extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value)); Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
renderScope.drawText(TITLE_SCALE, "PREVIEW", PADDING, PADDING - 2, Theme.FONT.value, Theme.getMutedFg()); renderScope.drawText(TITLE_SCALE, "PREVIEW", PADDING, PADDING - 2, Theme.FONT.value, Theme.getMutedFg());
@@ -45,7 +45,7 @@ public class OptionList extends Element {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1,
Theme.withAlpha(0.97f, Theme.BACKGROUND.value), Theme.withAlpha(0.14f, Theme.FOREGROUND.value)); Theme.getPopupBg(), Theme.withAlpha(0.14f, Theme.FOREGROUND.value));
int visible = Math.min(options.length, MAX_VISIBLE); int visible = Math.min(options.length, MAX_VISIBLE);
hovered = getIndexAt(ctx.mouseX, ctx.mouseY); hovered = getIndexAt(ctx.mouseX, ctx.mouseY);
@@ -13,6 +13,8 @@ import org.saturnclient.ui.RenderScope;
public class AnimationStagger extends Element { public class AnimationStagger extends Element {
protected List<Element> children = new ArrayList<>(); protected List<Element> children = new ArrayList<>();
public int delay; public int delay;
/** When the first child starts, in ms */
public int start = 0;
public AnimationStagger(int delay) { public AnimationStagger(int delay) {
this.delay = delay; this.delay = delay;
@@ -22,13 +24,21 @@ public class AnimationStagger extends Element {
this.delay = config.stagger.value; this.delay = config.stagger.value;
} }
/**
* Holds the children back until {@code start} ms after the screen opens.
*/
public AnimationStagger after(int start) {
this.start = start;
return this;
}
public void draw(Element element) { public void draw(Element element) {
if (!Config.stagger.value) { if (!Config.stagger.value) {
element.animation = null; element.animation = null;
} }
if (element.animation != null) { if (element.animation != null) {
element.animation.delay = delay * children.size(); element.animation.delay = start + delay * children.size();
} }
ElementRenderer.draw(children, element); ElementRenderer.draw(children, element);
@@ -1,5 +1,6 @@
package org.saturnclient.ui.elements; package org.saturnclient.ui.elements;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.ElementContext;
@@ -38,8 +39,8 @@ public class Button extends Element {
* The button's surface, shared with {@link IconButton}. * The button's surface, shared with {@link IconButton}.
*/ */
static void drawSurface(RenderScope renderScope, int width, int height, boolean hovered) { static void drawSurface(RenderScope renderScope, int width, int height, boolean hovered) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value, 1,
hovered ? Theme.withAlpha(0.9f, Theme.PRIMARY.value) : Theme.getPanelBg(), hovered ? Theme.PRIMARY.value : Theme.getPanelBg(),
Theme.withAlpha(hovered ? 0.25f : 0.1f, Theme.FOREGROUND.value)); Theme.withAlpha(hovered ? 0.25f : 0.1f, Theme.FOREGROUND.value));
} }
@@ -20,9 +20,9 @@ public class ImageTexture extends Element {
if (sprite.toString().endsWith(".svg")) { if (sprite.toString().endsWith(".svg")) {
MinecraftClientRef client = Providers.saturn.getClient(); MinecraftClientRef client = Providers.saturn.getClient();
int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWidth()); int renderWidth = (int) (width * client.getWindow().getFramebufferWidth() / client.getWindow().getWindowWidth());
int renderHeight = (int) (height * client.getWindow().getFramebufferHeight() int renderHeight = (int) (height * client.getWindow().getFramebufferHeight()
/ client.getWindow().getHeight()); / client.getWindow().getWindowHeight());
IdentifierRef pngId = SvgTexture.getSvg(sprite, renderWidth * 2, renderHeight * 2); IdentifierRef pngId = SvgTexture.getSvg(sprite, renderWidth * 2, renderHeight * 2);
@@ -1,12 +1,14 @@
package org.saturnclient.ui.elements; package org.saturnclient.ui.elements;
import org.saturnclient.config.Config;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.ElementContext;
import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.RenderScope;
/** /**
* The translucent, outlined surface that screens lay their content on. * The translucent, outlined surface that screens lay their content on,
* frosted over the game where glass is supported.
*/ */
public class Panel extends Element { public class Panel extends Element {
@@ -16,7 +18,7 @@ public class Panel extends Element {
} }
public static void drawBackground(RenderScope renderScope, int width, int height) { public static void drawBackground(RenderScope renderScope, int width, int height) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.BG_RADIUS.value, 1, renderScope.drawGlassRectangle(0, 0, width, height, Theme.BG_RADIUS.value, Config.menuBlur.value, 1,
Theme.getPanelBg(), Theme.getBorder()); Theme.getPanelBg(), Theme.getBorder());
} }
} }
@@ -7,6 +7,7 @@ import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.common.ref.asset.IdentifierRef;
import org.saturnclient.config.AnimationConfig; import org.saturnclient.config.AnimationConfig;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.cosmetics.Emotes; import org.saturnclient.cosmetics.Emotes;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
@@ -43,6 +44,11 @@ public class EmoteWheel extends SaturnScreen {
super("Emote Wheel"); super("Emote Wheel");
} }
@Override
protected FloatProperty getScaleSetting() {
return Config.hudMenuScale;
}
@Override @Override
public void ui() { public void ui() {
List<String> emotes = Emotes.availableEmotes; List<String> emotes = Emotes.availableEmotes;
@@ -125,8 +131,8 @@ public class EmoteWheel extends SaturnScreen {
@Override @Override
public void render(RenderScope renderScope, ElementContext ctx) { public void render(RenderScope renderScope, ElementContext ctx) {
renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, 1, renderScope.drawGlassRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, Config.widgetBlur.value,
Theme.getPanelBg(), Theme.getBorder()); 1, Theme.getPanelBg(), Theme.getBorder());
float titleScale = 0.54f; float titleScale = 0.54f;
drawCentered(renderScope, "EMOTES", titleScale, pages > 1 ? 24 : (height - 10) / 2, Theme.getMutedFg()); drawCentered(renderScope, "EMOTES", titleScale, pages > 1 ? 24 : (height - 10) / 2, Theme.getMutedFg());
@@ -18,8 +18,8 @@ import org.saturnclient.ui.resources.Fonts;
* Moves and resizes the enabled HUD mods, drawn as they are in game. Drag one * Moves and resizes the enabled HUD mods, drawn as they are in game. Drag one
* to move it, or drag its corner handle to resize it. * to move it, or drag its corner handle to resize it.
* *
* Unlike other Saturn screens this works in GUI pixels, like the HUD itself: * Unlike other Saturn screens this works in GUI pixels, like the HUD itself,
* it draws without the half scale and takes the mouse unscaled. * whatever Saturn's GUI scale.
*/ */
public class HudEditor extends SaturnScreen { public class HudEditor extends SaturnScreen {
private static final String HINT = "Drag to move · Drag the corner to resize · Esc when done"; private static final String HINT = "Drag to move · Drag the corner to resize · Esc when done";
@@ -45,6 +45,11 @@ public class HudEditor extends SaturnScreen {
} }
} }
@Override
public float getScale() {
return 1f;
}
@Override @Override
public void ui() { public void ui() {
// Placing HUDs needs a clear view of the game. // Placing HUDs needs a clear view of the game.
@@ -102,10 +107,7 @@ public class HudEditor extends SaturnScreen {
@Override @Override
public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) { public void render(RenderScope renderScope, int mouseX, int mouseY, float delta, long elapsed) {
// The screen hands over the mouse in Saturn units, twice GUI pixels. HudMod hovered = draggingMod != null ? draggingMod : getModAt(mouseX, mouseY);
int guiMouseX = mouseX / 2;
int guiMouseY = mouseY / 2;
HudMod hovered = draggingMod != null ? draggingMod : getModAt(guiMouseX, guiMouseY);
for (HudMod mod : hudMods) { for (HudMod mod : hudMods) {
ModLayout dim = mod.getDimensions(); ModLayout dim = mod.getDimensions();
@@ -149,12 +151,11 @@ public class HudEditor extends SaturnScreen {
} }
private void drawHint(RenderScope renderScope) { private void drawHint(RenderScope renderScope) {
int guiWidth = width / 2;
int textWidth = (int) (Fonts.getWidth(HINT, Theme.getBodyFont()) * HINT_SCALE); int textWidth = (int) (Fonts.getWidth(HINT, Theme.getBodyFont()) * HINT_SCALE);
int textHeight = (int) (Fonts.getHeight() * HINT_SCALE); int textHeight = (int) (Fonts.getHeight() * HINT_SCALE);
int pillWidth = textWidth + 20; int pillWidth = textWidth + 20;
int pillHeight = textHeight + 10; int pillHeight = textHeight + 10;
int x = (guiWidth - pillWidth) / 2; int x = (width - pillWidth) / 2;
int y = 8; int y = 8;
renderScope.drawRoundedRectangle(x, y, pillWidth, pillHeight, pillHeight / 2, 1, Theme.getPanelBg(), renderScope.drawRoundedRectangle(x, y, pillWidth, pillHeight, pillHeight / 2, 1, Theme.getPanelBg(),
@@ -176,13 +177,11 @@ public class HudEditor extends SaturnScreen {
} }
private void keepOnScreen(ModLayout dim) { private void keepOnScreen(ModLayout dim) {
int guiWidth = width / 2;
int guiHeight = height / 2;
int w = right(dim) - dim.x.value; int w = right(dim) - dim.x.value;
int h = bottom(dim) - dim.y.value; int h = bottom(dim) - dim.y.value;
dim.x.value = Math.max(0, Math.min(dim.x.value, guiWidth - w)); dim.x.value = Math.max(0, Math.min(dim.x.value, width - w));
dim.y.value = Math.max(0, Math.min(dim.y.value, guiHeight - h)); dim.y.value = Math.max(0, Math.min(dim.y.value, height - h));
} }
private static int right(ModLayout dim) { private static int right(ModLayout dim) {
@@ -3,6 +3,7 @@ package org.saturnclient.ui.screens;
import org.saturnclient.common.provider.Providers; import org.saturnclient.common.provider.Providers;
import org.saturnclient.config.AnimationConfig; import org.saturnclient.config.AnimationConfig;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.anim.Fade; import org.saturnclient.ui.anim.Fade;
import org.saturnclient.ui.anim.SlideY; import org.saturnclient.ui.anim.SlideY;
@@ -24,6 +25,11 @@ public class ShiftMenu extends SaturnScreen {
super("Shift Menu"); super("Shift Menu");
} }
@Override
protected FloatProperty getScaleSetting() {
return Config.hudMenuScale;
}
@Override @Override
public void ui() { public void ui() {
draw(new ImageTexture(Textures.LOGO_TEXT) draw(new ImageTexture(Textures.LOGO_TEXT)
@@ -6,9 +6,11 @@ import org.saturnclient.common.provider.Providers;
import org.saturnclient.common.ref.game.MinecraftClientRef.MinecraftScreen; import org.saturnclient.common.ref.game.MinecraftClientRef.MinecraftScreen;
import org.saturnclient.config.AnimationConfig; import org.saturnclient.config.AnimationConfig;
import org.saturnclient.config.Config; import org.saturnclient.config.Config;
import org.saturnclient.config.property.FloatProperty;
import org.saturnclient.config.Theme; import org.saturnclient.config.Theme;
import org.saturnclient.ui.Element; import org.saturnclient.ui.Element;
import org.saturnclient.ui.SaturnScreen; import org.saturnclient.ui.SaturnScreen;
import org.saturnclient.ui.anim.Animation;
import org.saturnclient.ui.anim.Fade; import org.saturnclient.ui.anim.Fade;
import org.saturnclient.ui.anim.SlideFade; import org.saturnclient.ui.anim.SlideFade;
import org.saturnclient.ui.anim.SlideY; import org.saturnclient.ui.anim.SlideY;
@@ -16,96 +18,133 @@ import org.saturnclient.ui.components.AccountChip;
import org.saturnclient.ui.components.Dock; import org.saturnclient.ui.components.Dock;
import org.saturnclient.ui.elements.AnimationStagger; import org.saturnclient.ui.elements.AnimationStagger;
import org.saturnclient.ui.elements.Button; import org.saturnclient.ui.elements.Button;
import org.saturnclient.ui.elements.IconButton;
import org.saturnclient.ui.elements.ImageTexture; import org.saturnclient.ui.elements.ImageTexture;
import org.saturnclient.ui.elements.Label; import org.saturnclient.ui.elements.Label;
import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Fonts;
import org.saturnclient.ui.resources.Textures; import org.saturnclient.ui.resources.Textures;
/** /**
* Saturn's title screen: the logo over the game's menus in the middle, and * Saturn's title screen: the logo over the game's menus in the middle, the
* along the bottom the player, a dock of Saturn's screens and the version. * player and a quit button in the top corners, and along the bottom a dock
* (Saturn's settings, its mods and cosmetics, then Minecraft's options) and the version.
*/ */
public class TitleMenu extends SaturnScreen { public class TitleMenu extends SaturnScreen {
private static final int LOGO = 88; // The logo and wordmark as the shift menu has them: the wordmark over the logo's foot
private static final int WORDMARK_WIDTH = 200; private static final int LOGO = 98;
private static final int WORDMARK_HEIGHT = 20; private static final int WORDMARK_WIDTH = 98;
private static final int WORDMARK_HEIGHT = 10;
private static final int WORDMARK_Y = 88;
/** From the logo's foot to the menus, close so they read as one block */
private static final int MENUS_GAP = 14;
private static final int COLUMN_WIDTH = 280; private static final int COLUMN_WIDTH = 280;
private static final int BUTTON_HEIGHT = 36; private static final int BUTTON_HEIGHT = 36;
private static final int GAP = 8; private static final int GAP = 8;
private static final int EDGE = 28; /** Space around the account chip, quit button, dock and version, from the screen's edges */
private static final float VERSION_SCALE = 0.62f; private static final int EDGE = 14;
/** The top corners sit a little tighter */
private static final int TOP_EDGE = 10;
/** As tall as the account chip beside it */
private static final int QUIT_SIZE = AccountChip.HEIGHT;
// The intro comes in in reading order, left to right and top to bottom, one step apart
private static final int STEP_ACCOUNT = 0;
private static final int STEP_QUIT = 1;
private static final int STEP_LOGO = 2;
private static final int STEP_MENUS = 3;
/** After the two menus */
private static final int STEP_VERSION = 5;
private static final int STEP_DOCK = 6;
private static final float VERSION_SCALE = 0.5f;
private static final float VERSION_ALPHA = 0.6f;
public TitleMenu() { public TitleMenu() {
super("Title Menu"); super("Title Menu");
} }
@Override
protected FloatProperty getScaleSetting() {
return Config.titleScale;
}
@Override @Override
public void ui() { public void ui() {
blurBackground = false; blurBackground = false;
// Logo and menus, centered together a little above the middle // Logo and menus, centered together a little above the middle
int menusHeight = BUTTON_HEIGHT * 3 + GAP * 2; int menusHeight = BUTTON_HEIGHT * 2 + GAP;
int blockHeight = LOGO + 16 + WORDMARK_HEIGHT + 36 + menusHeight; int blockHeight = LOGO + MENUS_GAP + menusHeight;
int top = (height - blockHeight) / 2 - 20; int top = (height - blockHeight) / 2 - 20;
draw(new ImageTexture(Config.getLogo()) draw(new ImageTexture(Config.getLogo())
.dimensions(LOGO, LOGO) .dimensions(LOGO, LOGO)
.position((width - LOGO) / 2, top) .position((width - LOGO) / 2, top)
.animation(new SlideY(AnimationConfig.logo, -20))); .animation(step(new SlideY(AnimationConfig.logo, -20), STEP_LOGO)));
draw(new ImageTexture(Textures.LOGO_TEXT_BIG) draw(new ImageTexture(Textures.LOGO_TEXT)
.dimensions(WORDMARK_WIDTH, WORDMARK_HEIGHT) .dimensions(WORDMARK_WIDTH, WORDMARK_HEIGHT)
.position((width - WORDMARK_WIDTH) / 2, top + LOGO + 16) .position((width - WORDMARK_WIDTH) / 2, top + WORDMARK_Y)
.animation(new Fade(AnimationConfig.logo.duration.value))); .animation(step(new Fade(AnimationConfig.logo.duration.value), STEP_LOGO)));
int rowHeight = BUTTON_HEIGHT + GAP; AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu)
int halfWidth = (COLUMN_WIDTH - GAP) / 2; .after(stepDelay(STEP_MENUS));
AnimationStagger menus = new AnimationStagger(AnimationConfig.mainMenu);
addMenuButton(menus, new Button("Singleplayer", addMenuButton(menus, new Button("Singleplayer",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld)) () -> Providers.saturn.getClient().setScreen(MinecraftScreen.SelectWorld))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0)); .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, 0));
addMenuButton(menus, new Button("Multiplayer", addMenuButton(menus, new Button("Multiplayer",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer)) () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Multiplayer))
.dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, rowHeight)); .dimensions(COLUMN_WIDTH, BUTTON_HEIGHT).position(0, BUTTON_HEIGHT + GAP));
addMenuButton(menus, new Button("Options",
() -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))
.dimensions(halfWidth, BUTTON_HEIGHT).position(0, rowHeight * 2));
addMenuButton(menus, new Button("Quit",
() -> Providers.saturn.getClient().scheduleStop())
.dimensions(halfWidth, BUTTON_HEIGHT).position(COLUMN_WIDTH - halfWidth, rowHeight * 2));
draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + 16 + WORDMARK_HEIGHT + 36)); draw(menus.position((width - COLUMN_WIDTH) / 2, top + LOGO + MENUS_GAP));
// Along the bottom: the player, Saturn's screens, the version // Top corners: the player, and quitting the game
draw(new AccountChip() draw(new AccountChip()
.position(EDGE, height - EDGE - AccountChip.HEIGHT) .position(TOP_EDGE, TOP_EDGE)
.animation(new Fade(AnimationConfig.mainMenu))); .animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_ACCOUNT)));
draw(new IconButton(Textures.ICON_CLOSE, () -> Providers.saturn.getClient().scheduleStop())
.dimensions(QUIT_SIZE, QUIT_SIZE)
.position(width - TOP_EDGE - QUIT_SIZE, TOP_EDGE)
.animation(step(new SlideFade(AnimationConfig.mainMenu, 8), STEP_QUIT)));
// Along the bottom: the dock, Saturn first (its settings) and Minecraft's options last, and the version
Dock dock = new Dock(List.of( Dock dock = new Dock(List.of(
new Dock.Item(Textures.LOGO, () -> Providers.saturn.getClient().setScreen(new ClientSettings())),
new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())), new Dock.Item(Textures.ICON_MODS, () -> Providers.saturn.getClient().setScreen(new ModMenu())),
new Dock.Item(Textures.ICON_COSMETICS, new Dock.Item(Textures.ICON_COSMETICS,
() -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())), () -> Providers.saturn.getClient().setScreen(new CosmeticsMenu())),
new Dock.Item(Textures.ICON_HUD_EDITOR, new Dock.Item(Textures.ICON_SETTINGS,
() -> Providers.saturn.getClient().setScreen(new HudEditor())))); () -> Providers.saturn.getClient().setScreen(MinecraftScreen.Options))));
draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT) draw(dock.position((width - dock.width) / 2, height - EDGE - Dock.HEIGHT)
.animation(new SlideFade(AnimationConfig.mainMenu, 12))); .animation(step(new SlideFade(AnimationConfig.mainMenu, -12), STEP_DOCK)));
String version = getVersionText(); String version = getVersionText();
int versionWidth = (int) (Fonts.getWidth(version, Theme.getBodyFont()) * VERSION_SCALE);
int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE); int versionHeight = (int) (Fonts.getHeight() * VERSION_SCALE);
draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont, Theme::getMutedFg) draw(new Label(() -> version, VERSION_SCALE, Theme::getBodyFont,
.position(width - EDGE - versionWidth, height - EDGE - (AccountChip.HEIGHT + versionHeight) / 2) () -> Theme.withAlpha(VERSION_ALPHA, Theme.getMutedFg()))
.animation(new Fade(AnimationConfig.mainMenu))); .position(EDGE, height - EDGE - versionHeight)
.animation(step(new Fade(AnimationConfig.mainMenu), STEP_VERSION)));
} }
/** /**
* "Saturn Client 0.1.0 · Minecraft 1.21.11", from the mod version * When the intro's {@code step} starts, in ms: the Main Menu stagger apart,
* "0.1.0+1.21.11". * or all at once when staggering is off.
*/
private static int stepDelay(int step) {
return Config.stagger.value ? step * AnimationConfig.mainMenu.stagger.value : 0;
}
private static Animation step(Animation animation, int step) {
animation.delay = stepDelay(step);
return animation;
}
/**
* "Saturn 0.1.0 · 1.21.11", from the mod version "0.1.0+1.21.11".
*/ */
private static String getVersionText() { private static String getVersionText() {
String[] version = Providers.saturn.getModVersion().split("\\+", 2); String[] version = Providers.saturn.getModVersion().split("\\+", 2);
String text = "Saturn Client " + version[0]; String text = "Saturn " + version[0];
return version.length > 1 ? text + " · Minecraft " + version[1] : text; return version.length > 1 ? text + " · " + version[1] : text;
} }
private static void addMenuButton(AnimationStagger stagger, Element button) { private static void addMenuButton(AnimationStagger stagger, Element button) {
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,193 @@
{
"version": 3,
"name": "\u0414\u044d\u0431",
"description": "\u0414\u044d\u0431\u043d\u0438 \u0441\u043e \u0432\u0441\u0435\u043c \u0432\u043a\u0443\u0441\u043e\u043c",
"author": "SPEmotes",
"emote": {
"beginTick": 0,
"endTick": 30,
"stopTick": 31,
"isLoop": false,
"returnTick": 1,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 4,
"easing": "EASEINOUTQUAD",
"rightLeg": {
"x": -1.873698,
"y": 11.100854,
"z": 0.104908,
"pitch": -0.01179,
"yaw": 0.000183,
"roll": 0.037555
}
},
{
"tick": 7,
"easing": "EASEINOUTQUAD",
"rightArm": {
"x": -5.06384,
"y": 3.129459,
"z": -0.028821,
"pitch": 1.006445,
"yaw": 0.326442,
"roll": 1.238234
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -1.859405,
"yaw": 0.56235,
"roll": -1.6982
}
},
{
"tick": 8,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.14541,
"yaw": 0.336522,
"roll": -0.066829
},
"body": {
"x": 0.0,
"y": 0.010776,
"z": 0.0,
"pitch": -0.007742,
"yaw": -0.304674,
"roll": -0.059173
},
"rightArm": {
"bend": -1.042365
},
"leftArm": {
"bend": -1.205889
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": -0.023581,
"yaw": 0.001468,
"roll": 0.075111
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.025482,
"yaw": -0.467862,
"roll": 0.006087
}
},
{
"tick": 12,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"x": 1.933101,
"y": 11.441392,
"z": -0.084124,
"pitch": 0.021479,
"yaw": -0.538523,
"roll": -0.038416
}
},
{
"tick": 16,
"easing": "EASEINOUTQUAD",
"body": {
"x": 0.0,
"y": 0.010776,
"z": 0.0,
"pitch": -0.043213,
"yaw": 0.303575,
"roll": -0.059152
},
"rightArm": {
"bend": -1.396377
},
"leftArm": {
"bend": 0.0
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": -0.018974,
"yaw": 0.303724,
"roll": 0.068059
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.017477,
"yaw": -0.548618,
"roll": -0.082919
}
},
{
"tick": 18,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.291391,
"yaw": 0.542094,
"roll": -0.159259
},
"rightArm": {
"x": -5.06384,
"y": 3.129459,
"z": -0.028821,
"pitch": -1.265057,
"yaw": 0.037433,
"roll": 1.364253
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -0.438223,
"yaw": -0.045192,
"roll": -1.798213
}
},
{
"tick": 21,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.291391,
"yaw": 0.542094,
"roll": -0.159259
},
"rightArm": {
"x": -5.06384,
"y": 3.129459,
"z": -0.028821,
"pitch": -1.265057,
"yaw": 0.037433,
"roll": 1.364253
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -0.438223,
"yaw": -0.045192,
"roll": -1.798213
}
}
]
}
}
@@ -1,697 +0,0 @@
{
"version": 1,
"valid": true,
"name": "front_flip",
"description": "Parkour!!!!1",
"author": "DKinchik",
"emote": {
"beginTick": 0,
"endTick": 17,
"stopTick": 18,
"isLoop": false,
"returnTick": 1,
"nsfw": false,
"degrees": false,
"moves": [
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"x": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"x": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"y": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"y": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"z": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"z": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"pitch": -0.0
}
},
{
"tick": 5,
"easing": "INOUTQUAD",
"head": {
"pitch": 0.7020477
}
},
{
"tick": 10,
"easing": "INOUTQUAD",
"head": {
"pitch": 0.7020477
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"pitch": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"yaw": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"yaw": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"head": {
"roll": 0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"head": {
"roll": 0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"x": -1.9
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"x": -1.9
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"y": 11.169379
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"rightLeg": {
"y": 12.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"y": 12.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"z": -0.39214998
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"rightLeg": {
"z": 0.1
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"z": 0.1
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"pitch": -0.0
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"rightLeg": {
"pitch": -0.0
}
},
{
"tick": 5,
"easing": "INOUTQUAD",
"rightLeg": {
"pitch": -1.9707682
}
},
{
"tick": 10,
"easing": "INOUTQUAD",
"rightLeg": {
"pitch": -1.9707682
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"pitch": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"yaw": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"yaw": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"roll": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightLeg": {
"roll": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightLeg": {
"bend": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"x": -5.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"x": -5.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"y": 2.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"y": 2.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"z": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"z": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"pitch": -0.0
}
},
{
"tick": 5,
"easing": "INOUTQUAD",
"rightArm": {
"pitch": -1.3665518
}
},
{
"tick": 10,
"easing": "INOUTQUAD",
"rightArm": {
"pitch": -1.3665518
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"pitch": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"yaw": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"yaw": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"roll": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"rightArm": {
"roll": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"rightArm": {
"bend": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"x": 5.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"x": 5.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"y": 2.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"y": 2.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"z": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"z": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"pitch": 0.0
}
},
{
"tick": 5,
"easing": "INOUTQUAD",
"leftArm": {
"pitch": -1.0793308
}
},
{
"tick": 10,
"easing": "INOUTQUAD",
"leftArm": {
"pitch": -1.0793308
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"pitch": 0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"yaw": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"yaw": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"roll": 0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftArm": {
"roll": 0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftArm": {
"bend": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"x": 1.9
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"x": 1.9
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"y": 10.861001
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"leftLeg": {
"y": 12.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"y": 12.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"z": -0.5082285
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"leftLeg": {
"z": -0.1
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"z": -0.1
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"pitch": -0.0
}
},
{
"tick": 3,
"easing": "INOUTQUAD",
"leftLeg": {
"pitch": -0.0
}
},
{
"tick": 5,
"easing": "INOUTQUAD",
"leftLeg": {
"pitch": -1.9850811
}
},
{
"tick": 10,
"easing": "INOUTQUAD",
"leftLeg": {
"pitch": -1.9850811
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"pitch": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"yaw": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"yaw": -0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"roll": -0.0
}
},
{
"tick": 15,
"easing": "INOUTQUAD",
"leftLeg": {
"roll": 0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"leftLeg": {
"bend": -0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"x": 0.0
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"x": 0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"y": -0.061333913
}
},
{
"tick": 7,
"easing": "OUTQUAD",
"body": {
"y": 0.48852682
}
},
{
"tick": 15,
"easing": "CONSTANT",
"body": {
"y": 0.0
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"y": 0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"z": -0.0
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"z": -0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"pitch": 0.0
}
},
{
"tick": 7,
"easing": "OUTQUAD",
"body": {
"pitch": -2.8362873
}
},
{
"tick": 15,
"easing": "CONSTANT",
"body": {
"pitch": -6.2831855
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"pitch": 0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"yaw": 0.0
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"yaw": 0.0
}
},
{
"tick": 2,
"easing": "INQUAD",
"body": {
"roll": -0.0
}
},
{
"tick": 16,
"easing": "INQUAD",
"body": {
"roll": 0.0
}
},
{
"tick": 2,
"easing": "INOUTQUAD",
"body": {
"bend": -0.0
}
}
]
}
}
@@ -0,0 +1,825 @@
{
"version": 3,
"name": "Get the Griddy",
"description": "fortnite battlepass",
"author": "SeroRonin",
"emote": {
"beginTick": 0,
"endTick": 91,
"stopTick": 91,
"isLoop": true,
"returnTick": 2,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 1,
"easing": "LINEAR",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.261799,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"pitch": -0.872665,
"yaw": -0.436332,
"roll": 0.349066
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": -0.523599,
"yaw": 0.0,
"roll": 0.0,
"bend": 0.436332
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": -0.174533,
"yaw": 0.0,
"roll": 0.0,
"bend": 0.523599
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -0.872674,
"yaw": 0.436332,
"roll": -0.349066
},
"body": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.077608,
"yaw": 0.0,
"roll": 0.0
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 4,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 5,
"easing": "LINEAR",
"body": {
"x": 0.0,
"y": 0.0375,
"z": 0.0,
"pitch": 0.033951,
"yaw": 0.0,
"roll": 0.0
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 6,
"easing": "LINEAR",
"head": {
"pitch": 0.191986
},
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
}
},
{
"tick": 9,
"easing": "LINEAR",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.261799,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"pitch": 0.523599,
"yaw": -0.436332,
"roll": 0.226893
},
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.52359,
"yaw": 0.436332,
"roll": -0.226893
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 12,
"easing": "LINEAR",
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": -0.610865,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 13,
"easing": "LINEAR",
"head": {
"pitch": 0.191986
},
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 16,
"easing": "LINEAR",
"head": {
"pitch": 0.261799,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"pitch": -0.872665,
"roll": 0.349066
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": -0.523599,
"yaw": 0.0,
"roll": 0.0,
"bend": 0.436332
},
"rightLeg": {
"pitch": -0.174533,
"bend": 0.523599
},
"leftArm": {
"pitch": -0.872674,
"roll": -0.349066
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 19,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 20,
"easing": "LINEAR",
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 21,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
}
},
{
"tick": 24,
"easing": "LINEAR",
"head": {
"pitch": -0.416877,
"yaw": -0.519655,
"roll": 0.21801
},
"rightArm": {
"pitch": 0.523599,
"roll": 0.226893
},
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"leftArm": {
"pitch": 0.52359,
"roll": -0.226893
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 27,
"easing": "LINEAR",
"rightLeg": {
"pitch": -0.610865
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 28,
"easing": "LINEAR",
"head": {
"pitch": -0.457451,
"roll": 0.21801
},
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 31,
"easing": "LINEAR",
"head": {
"pitch": -0.416877,
"roll": 0.21801
},
"rightArm": {
"pitch": -0.872665,
"roll": 0.349066
},
"leftLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"rightLeg": {
"pitch": -0.174533,
"bend": 0.523599
},
"leftArm": {
"pitch": -0.872674,
"roll": -0.349066
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 34,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 35,
"easing": "LINEAR",
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 36,
"easing": "LINEAR",
"head": {
"pitch": -0.457451,
"roll": 0.21801
},
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
}
},
{
"tick": 39,
"easing": "LINEAR",
"head": {
"pitch": -0.416877,
"roll": 0.21801
},
"rightArm": {
"pitch": 0.523599,
"roll": 0.226893
},
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"leftArm": {
"pitch": 0.52359,
"roll": -0.226893
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 42,
"easing": "LINEAR",
"rightLeg": {
"pitch": -0.610865
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 43,
"easing": "LINEAR",
"head": {
"pitch": -0.457451,
"roll": 0.21801
},
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 46,
"easing": "LINEAR",
"head": {
"pitch": -0.416877,
"yaw": -0.519655,
"roll": 0.21801
},
"rightArm": {
"pitch": -0.872665,
"roll": 0.349066,
"bend": 0.0
},
"leftLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"rightLeg": {
"pitch": -0.174533,
"bend": 0.523599
},
"leftArm": {
"pitch": -0.872674,
"roll": -0.349066,
"bend": 0.0
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 49,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 50,
"easing": "LINEAR",
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 51,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
}
},
{
"tick": 54,
"easing": "LINEAR",
"head": {
"pitch": -0.000488,
"yaw": -0.272548,
"roll": -0.027396
},
"rightArm": {
"pitch": 0.523599,
"roll": 0.226893,
"bend": -0.872665
},
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"leftArm": {
"pitch": 0.52359,
"roll": -0.226893,
"bend": -0.872665
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 57,
"easing": "LINEAR",
"rightLeg": {
"pitch": -0.610865
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 58,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 61,
"easing": "LINEAR",
"rightArm": {
"pitch": -0.872665,
"yaw": -0.436332,
"roll": 0.349066
},
"leftLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"rightLeg": {
"pitch": -0.174533,
"bend": 0.523599
},
"leftArm": {
"pitch": -0.872674,
"yaw": 0.436332,
"roll": -0.349066
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 64,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 65,
"easing": "LINEAR",
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 66,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
}
},
{
"tick": 69,
"easing": "LINEAR",
"head": {
"pitch": -0.523599,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"pitch": -2.356194,
"yaw": 0.087266,
"roll": 0.523599
},
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"leftArm": {
"pitch": -2.338528,
"yaw": -0.087266,
"roll": -0.523599
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 72,
"easing": "LINEAR",
"rightLeg": {
"pitch": -0.610865
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 73,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 76,
"easing": "LINEAR",
"rightArm": {
"bend": -0.959931
},
"leftLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"rightLeg": {
"pitch": -0.174533,
"bend": 0.523599
},
"leftArm": {
"bend": -0.959931
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 79,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.610865
}
},
{
"tick": 80,
"easing": "LINEAR",
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 81,
"easing": "LINEAR",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": -0.523599,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"pitch": -2.181662,
"yaw": 0.0,
"roll": 0.436332
},
"leftLeg": {
"bend": 0.959931
},
"rightLeg": {
"bend": 0.523599
},
"leftArm": {
"pitch": -2.185317,
"yaw": 0.0,
"roll": -0.436332
}
},
{
"tick": 84,
"easing": "LINEAR",
"leftLeg": {
"pitch": -0.178507,
"bend": 0.523599
},
"rightLeg": {
"pitch": -0.523599,
"bend": 0.436332
},
"body": {
"y": 0.0,
"pitch": 0.077608
},
"torso": {
"bend": 0.395397
}
},
{
"tick": 87,
"easing": "LINEAR",
"rightLeg": {
"pitch": -0.610865
},
"body": {
"y": 0.0375,
"pitch": 0.033951
},
"torso": {
"bend": 0.377793
}
},
{
"tick": 88,
"easing": "LINEAR",
"leftLeg": {
"bend": 0.523599
},
"rightLeg": {
"bend": 0.959931
}
},
{
"tick": 91,
"easing": "LINEAR",
"head": {
"pitch": 0.261799
},
"rightArm": {
"pitch": -0.872665,
"yaw": -0.436332,
"roll": 0.349066,
"bend": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": -0.523599,
"yaw": 0.0,
"roll": 0.0,
"bend": 0.436332
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": -0.174533,
"yaw": 0.0,
"roll": 0.0,
"bend": 0.523599
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -0.872674,
"yaw": 0.436332,
"roll": -0.349066,
"bend": 0.0
},
"body": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.077608,
"yaw": 0.0,
"roll": 0.0
},
"torso": {
"bend": 0.395397
}
}
]
}
}
@@ -0,0 +1,121 @@
{
"version": 3,
"name": "Salute",
"description": "Hand behind back",
"author": "Wah0o",
"emote": {
"beginTick": 0,
"endTick": 30,
"stopTick": 30,
"isLoop": true,
"returnTick": 12,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 10,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.484846,
"yaw": 0.0,
"roll": -0.576996
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -2.604544,
"yaw": 0.0,
"roll": -0.400639
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
},
{
"tick": 30,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.484846,
"yaw": 0.0,
"roll": -0.576996
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": -2.604544,
"yaw": 0.0,
"roll": -0.400639
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
}
]
}
}
@@ -0,0 +1,90 @@
{
"version": 3,
"name": "Shrug",
"description": "don't care",
"author": "Wah0o",
"emote": {
"beginTick": 0,
"endTick": 30,
"stopTick": 31,
"isLoop": false,
"returnTick": 1,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 10,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.537013,
"roll": 0.0
}
},
{
"tick": 20,
"easing": "EASEINOUTQUAD",
"head": {
"yaw": -0.449322
},
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
},
{
"tick": 25,
"easing": "EASEINOUTQUAD",
"rightArm": {
"y": 0.673667
},
"leftArm": {
"y": 0.738915
}
},
{
"tick": 30,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
}
]
}
}
@@ -0,0 +1,307 @@
{
"version": 3,
"name": "Take The L",
"description": "Savor the W",
"author": "Landro",
"emote": {
"beginTick": 0,
"endTick": 30,
"stopTick": 30,
"isLoop": true,
"returnTick": 10,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 5,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"y": 0.25,
"roll": 0.0
},
"leftArm": {
"x": 5.76,
"y": 3.28,
"z": -0.64,
"pitch": -1.294932,
"yaw": 0.991577,
"roll": -1.205874,
"bend": -1.057819
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.261799,
"roll": 0.0,
"bend": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": -0.523599,
"roll": -0.785398
}
},
{
"tick": 8,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"x": 2.9,
"y": 11.0,
"z": -1.1,
"yaw": -0.959931,
"roll": -1.396263,
"bend": 0.785398
}
},
{
"tick": 10,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.030844,
"yaw": 0.0,
"roll": -0.059719
},
"body": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": -0.087266
},
"rightArm": {
"x": -7.0,
"y": -2.0,
"z": -2.0,
"pitch": -1.416631,
"yaw": -0.785398,
"roll": 4.110391,
"bend": 1.308997
},
"leftArm": {
"x": 5.76,
"y": 3.28,
"z": -0.64,
"pitch": -1.294932,
"yaw": 0.991577,
"roll": -1.205874,
"bend": -1.057819
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": -0.9,
"pitch": -0.174533,
"yaw": 0.261799,
"roll": 0.174533,
"bend": 0.261799
},
"leftLeg": {
"x": 2.9,
"y": 11.0,
"z": -1.1,
"pitch": 0.0,
"yaw": -0.959931,
"roll": -1.570796,
"bend": 0.0
}
},
{
"tick": 12,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"bend": 0.785398
}
},
{
"tick": 15,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"y": 0.25,
"roll": 0.0
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.261799,
"roll": 0.0,
"bend": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": -0.261799,
"roll": 0.0,
"bend": 0.0
}
},
{
"tick": 18,
"easing": "EASEINOUTQUAD",
"rightLeg": {
"x": -2.9,
"y": 11.0,
"z": -0.9,
"yaw": 0.959931,
"roll": 1.396263,
"bend": 0.785398
}
},
{
"tick": 20,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.105365,
"yaw": 0.000794,
"roll": 0.055846
},
"body": {
"y": 0.0,
"roll": 0.087266
},
"rightArm": {
"pitch": -1.132143
},
"rightLeg": {
"x": -2.9,
"y": 11.0,
"z": -0.9,
"yaw": 0.959931,
"roll": 1.570796,
"bend": 0.0
},
"leftLeg": {
"pitch": -0.174533,
"roll": -0.174533,
"bend": 0.261799
}
},
{
"tick": 22,
"easing": "EASEINOUTQUAD",
"rightLeg": {
"bend": 0.785398
}
},
{
"tick": 25,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"body": {
"y": 0.25
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.261799,
"roll": 0.0,
"bend": 0.0
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": -0.261799,
"roll": 0.0,
"bend": 0.0
}
},
{
"tick": 28,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"x": 2.9,
"y": 11.0,
"z": -1.1,
"yaw": -0.959931,
"roll": -1.396263,
"bend": 0.785398
}
},
{
"tick": 30,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.030844,
"yaw": 0.0,
"roll": -0.059719
},
"body": {
"y": 0.0,
"roll": -0.087266
},
"rightArm": {
"x": -7.0,
"y": -2.0,
"z": -2.0,
"pitch": -1.416631,
"yaw": -0.785398,
"roll": 4.110391,
"bend": 1.308997
},
"leftArm": {
"x": 5.76,
"y": 3.28,
"z": -0.64,
"pitch": -1.294932,
"yaw": 0.991577,
"roll": -1.205874,
"bend": -1.057819
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": -0.9,
"pitch": -0.174533,
"yaw": 0.261799,
"roll": 0.174533,
"bend": 0.261799
},
"leftLeg": {
"x": 2.9,
"y": 11.0,
"z": -1.1,
"pitch": 0.0,
"yaw": -0.959931,
"roll": -1.570796,
"bend": 0.0
}
}
]
}
}
@@ -0,0 +1,685 @@
{
"version": 3,
"name": "Twerk",
"description": "Hips back, and bounce",
"author": "Saturn Client",
"emote": {
"degrees": true,
"isLoop": true,
"returnTick": 0,
"endTick": 12,
"stopTick": 13,
"moves": [
{
"tick": 0,
"easing": "LINEAR",
"body": {
"x": -0.0,
"y": 0.0637
},
"head": {
"x": 0.1873,
"y": 7.6654,
"z": -3.9561,
"pitch": 28.4994,
"yaw": 0.5013,
"roll": -0.9013
},
"torso": {
"x": 0.1873,
"y": 7.6654,
"z": -3.9561,
"pitch": 43.8857,
"yaw": -0.2748,
"roll": -0.5704,
"bend": 32.4225,
"axis": 178.4861
},
"rightArm": {
"x": -4.8056,
"y": 9.1559,
"z": -2.5937,
"pitch": 41.2104,
"yaw": 11.1599,
"roll": 11.5835,
"bend": -28.9106,
"axis": -6.4245
},
"leftArm": {
"x": 5.1932,
"y": 9.0568,
"z": -2.5457,
"pitch": 41.2134,
"yaw": -10.0744,
"roll": -11.3678,
"bend": -34.2847,
"axis": 3.7104
},
"rightLeg": {
"x": -1.6738,
"y": 16.311,
"z": 4.4622,
"pitch": -81.0086,
"yaw": 25.937,
"roll": 11.6147,
"bend": -106.5341,
"axis": 178.6748
},
"leftLeg": {
"x": 2.1262,
"y": 16.311,
"z": 4.4622,
"pitch": -80.1541,
"yaw": -23.1741,
"roll": -15.9396,
"bend": -109.5512,
"axis": 186.6826
}
},
{
"tick": 1,
"easing": "LINEAR",
"head": {
"y": 7.3213,
"z": -3.9546
},
"torso": {
"y": 7.3213,
"z": -3.9546,
"pitch": 44.2163,
"roll": -1.2208,
"bend": 31.983
},
"rightArm": {
"y": 8.8595,
"z": -2.4569,
"pitch": 40.8328,
"roll": 6.2457,
"bend": -30.0698,
"axis": -6.2466
},
"leftArm": {
"y": 8.6462,
"z": -2.6643,
"pitch": 39.718,
"roll": -11.4772,
"bend": -31.5341,
"axis": 5.9068
},
"rightLeg": {
"y": 15.9102,
"pitch": -75.926,
"yaw": 24.5861,
"roll": 13.5142,
"bend": -102.1127
},
"leftLeg": {
"y": 15.9102,
"pitch": -75.5106,
"yaw": -27.5028,
"roll": -13.7351,
"bend": -102.8428,
"axis": 189.6313
}
},
{
"tick": 2,
"easing": "LINEAR",
"head": {
"x": -0.8055,
"y": 8.1689
},
"torso": {
"x": -0.8055,
"y": 8.1689,
"bend": 38.1002
},
"rightArm": {
"x": -5.6879,
"y": 9.7845,
"axis": -3.7942
},
"leftArm": {
"x": 4.2976,
"y": 9.2523,
"pitch": 44.6401,
"yaw": -6.835,
"roll": -16.9446,
"bend": -32.4677
},
"rightLeg": {
"y": 16.2659,
"pitch": -78.5245,
"yaw": 20.2182,
"roll": 19.5894,
"bend": -107.209
},
"leftLeg": {
"y": 16.2659,
"pitch": -79.0541,
"bend": -104.5866
}
},
{
"tick": 3,
"easing": "LINEAR",
"head": {
"x": -1.5256,
"y": 9.7364,
"z": -4.854,
"pitch": 34.1426
},
"torso": {
"x": -1.5256,
"y": 9.7364,
"z": -4.854,
"pitch": 51.4994,
"roll": -5.723,
"bend": 46.2958,
"axis": 171.0835
},
"rightArm": {
"x": -6.3053,
"y": 11.4659,
"z": -3.0779,
"pitch": 49.8778,
"yaw": 10.9541,
"roll": 9.4174,
"bend": -29.3297
},
"leftArm": {
"x": 3.6347,
"y": 10.4699,
"z": -3.5025,
"pitch": 51.8029,
"roll": -24.6351,
"axis": 3.6087
},
"rightLeg": {
"z": 4.6288,
"yaw": 18.5052,
"roll": 21.802,
"bend": -116.6488,
"axis": 171.6123
},
"leftLeg": {
"z": 4.6288,
"pitch": -87.1428,
"yaw": -23.4405,
"axis": 180.9108
}
},
{
"tick": 4,
"easing": "LINEAR",
"head": {
"x": -1.8731,
"y": 10.6957,
"z": -5.0099,
"pitch": 33.2442,
"yaw": 3.9201,
"roll": -6.0227
},
"torso": {
"x": -1.8731,
"y": 10.6957,
"z": -5.0099,
"pitch": 52.4228,
"yaw": 3.8511,
"roll": -5.9949,
"bend": 49.0159,
"axis": 172.2434
},
"rightArm": {
"x": -6.5993,
"y": 12.418,
"z": -3.0931,
"pitch": 51.1349,
"yaw": 8.0105,
"axis": -8.2902
},
"leftArm": {
"x": 3.3186,
"y": 11.3757,
"z": -3.7647,
"pitch": 53.8923,
"yaw": -6.0212,
"roll": -27.197,
"bend": -39.6549,
"axis": 5.9148
},
"rightLeg": {
"x": -2.3763,
"y": 17.9026,
"pitch": -93.1557,
"bend": -122.9048,
"axis": 170.6601
},
"leftLeg": {
"x": 1.4237,
"y": 17.9026,
"pitch": -93.1369,
"bend": -121.5363
}
},
{
"tick": 5,
"easing": "LINEAR",
"head": {
"x": -1.4047,
"y": 9.9389,
"pitch": 28.9007,
"yaw": 3.2148
},
"torso": {
"x": -1.4047,
"y": 9.9389,
"yaw": 3.7305,
"bend": 43.4407
},
"rightArm": {
"x": -6.2198,
"y": 11.5114,
"pitch": 48.4737,
"yaw": 7.9932,
"bend": -38.6811
},
"leftArm": {
"x": 3.7446,
"y": 10.9738,
"z": -3.474,
"roll": -23.505,
"bend": -36.714
},
"rightLeg": {
"y": 17.761,
"pitch": -92.4411,
"roll": 20.4558,
"bend": -121.9263
},
"leftLeg": {
"y": 17.761,
"pitch": -92.379,
"yaw": -26.2774,
"roll": -17.4598,
"bend": -120.6269,
"axis": 184.7551
}
},
{
"tick": 6,
"easing": "LINEAR",
"head": {
"x": -0.5445,
"y": 8.2919,
"z": -4.1286,
"pitch": 27.4449
},
"torso": {
"x": -0.5445,
"y": 8.2919,
"z": -4.1286,
"pitch": 44.9959,
"roll": -0.1331,
"bend": 35.1937
},
"rightArm": {
"x": -5.5003,
"y": 9.7189,
"z": -2.5814,
"yaw": 10.117,
"roll": 11.6263,
"bend": -36.6136,
"axis": -2.9818
},
"leftArm": {
"x": 4.4989,
"y": 9.6953,
"z": -2.8477,
"pitch": 43.3362,
"yaw": -10.8396,
"bend": -30.325
},
"rightLeg": {
"y": 16.7832,
"z": 4.4556,
"yaw": 20.579,
"axis": 174.8949
},
"leftLeg": {
"y": 16.7832,
"z": 4.4556,
"roll": -13.3635,
"axis": 181.9013
}
},
{
"tick": 7,
"easing": "LINEAR",
"head": {
"y": 7.335,
"z": -3.8955,
"yaw": -1.2543,
"roll": 1.9551
},
"torso": {
"y": 7.335,
"z": -3.8955,
"pitch": 43.6308,
"yaw": -0.6674,
"bend": 31.2968
},
"rightArm": {
"y": 8.7024,
"z": -2.5743,
"pitch": 39.7281,
"roll": 11.0094,
"bend": -32.4336,
"axis": -5.1024
},
"leftArm": {
"y": 8.8585,
"z": -2.4579,
"pitch": 40.3248,
"roll": -8.1427,
"bend": -29.1361,
"axis": 6.7593
},
"rightLeg": {
"x": -2.0654,
"y": 16.0076,
"pitch": -76.8625,
"yaw": 25.941,
"roll": 14.4188,
"bend": -105.4043,
"axis": 171.2628
},
"leftLeg": {
"x": 1.7346,
"y": 16.0076,
"pitch": -77.6408,
"yaw": -25.7135,
"roll": -11.5799,
"bend": -103.133,
"axis": 182.0395
}
},
{
"tick": 8,
"easing": "LINEAR",
"head": {
"x": 0.5149,
"y": 7.6114,
"z": -4.1189
},
"torso": {
"x": 0.5149,
"y": 7.6114,
"z": -4.1189,
"pitch": 45.5567,
"roll": 1.9,
"bend": 34.3817,
"axis": 185.8808
},
"rightArm": {
"x": -4.5647,
"y": 8.8445,
"z": -2.8129,
"pitch": 41.452,
"yaw": 7.1089,
"roll": 13.4728,
"bend": -31.5905,
"axis": -5.4945
},
"leftArm": {
"y": 9.1765,
"z": -2.5698,
"pitch": 42.5806,
"roll": -6.1009,
"bend": -30.5432
},
"rightLeg": {
"y": 16.0062,
"pitch": -76.3427,
"yaw": 27.2082,
"roll": 13.8298,
"bend": -102.5222,
"axis": 171.6254
},
"leftLeg": {
"y": 16.0062,
"pitch": -76.2516,
"bend": -103.6369
}
},
{
"tick": 9,
"easing": "LINEAR",
"torso": {
"axis": 188.354
},
"rightArm": {
"y": 9.8245,
"axis": -3.4265
},
"leftArm": {
"yaw": -11.6764,
"roll": -8.5612,
"bend": -29.2921,
"axis": 3.7762
},
"rightLeg": {
"y": 16.6561,
"z": 4.6178,
"yaw": 23.9791,
"bend": -108.5895,
"axis": 177.6256
},
"leftLeg": {
"y": 16.6561,
"z": 4.6178,
"pitch": -82.2218,
"yaw": -18.7129,
"roll": -21.3727
}
},
{
"tick": 10,
"easing": "LINEAR",
"head": {
"x": 1.7868,
"y": 10.3947,
"z": -4.9963,
"pitch": 34.2919,
"roll": 5.9583
},
"torso": {
"x": 1.7868,
"y": 10.3947,
"z": -4.9963,
"pitch": 52.5511,
"roll": 6.3235,
"bend": 48.7324,
"axis": 188.7341
},
"rightArm": {
"x": -3.3947,
"y": 11.0427,
"z": -3.69,
"pitch": 53.7658,
"yaw": 6.158,
"roll": 26.7903,
"bend": -38.4178
},
"leftArm": {
"x": 6.5243,
"y": 12.1424,
"z": -3.1327,
"pitch": 51.0522,
"bend": -30.9765,
"axis": 7.1451
},
"rightLeg": {
"x": -1.4452,
"y": 17.5819,
"pitch": -90.7807,
"bend": -118.2986,
"axis": 178.4542
},
"leftLeg": {
"x": 2.3548,
"y": 17.5819,
"pitch": -90.4714,
"roll": -21.4522,
"bend": -120.5366,
"axis": 189.2421
}
},
{
"tick": 11,
"easing": "LINEAR",
"head": {
"x": 1.7369,
"y": 10.5273,
"z": -4.8864,
"yaw": -3.8864,
"roll": 5.3801
},
"torso": {
"x": 1.7369,
"y": 10.5273,
"z": -4.8864,
"pitch": 51.13,
"yaw": -4.0928,
"bend": 47.0625
},
"rightArm": {
"x": -3.4451,
"y": 11.352,
"z": -3.6905,
"pitch": 52.2992,
"yaw": 6.6309,
"roll": 26.0007,
"bend": -39.1249,
"axis": -6.422
},
"leftArm": {
"x": 6.4911,
"y": 12.1855,
"pitch": 50.212,
"yaw": -7.4219,
"bend": -36.9681,
"axis": 7.7245
},
"rightLeg": {
"y": 17.9759,
"pitch": -93.724,
"yaw": 26.1749,
"roll": 17.9629,
"bend": -122.4062,
"axis": 175.2086
},
"leftLeg": {
"y": 17.9759,
"pitch": -93.8978,
"bend": -123.3927,
"axis": 188.3051
}
},
{
"tick": 12,
"easing": "LINEAR",
"head": {
"pitch": 27.4876
},
"rightArm": {
"y": 10.3597,
"roll": 19.9926
},
"leftArm": {
"bend": -38.4293,
"axis": 3.7703
},
"rightLeg": {
"y": 17.3175,
"pitch": -89.3376,
"bend": -116.6299
},
"leftLeg": {
"y": 17.3175,
"pitch": -89.0719,
"yaw": -19.2542,
"roll": -19.5116,
"bend": -118.7259,
"axis": 185.0963
}
},
{
"tick": 13,
"easing": "LINEAR",
"body": {
"x": -0.0,
"y": 0.0637
},
"head": {
"x": 0.1873,
"y": 7.6654,
"z": -3.9561,
"pitch": 28.4994,
"yaw": 0.5013,
"roll": -0.9013
},
"torso": {
"x": 0.1873,
"y": 7.6654,
"z": -3.9561,
"pitch": 43.8857,
"yaw": -0.2748,
"roll": -0.5704,
"bend": 32.4225,
"axis": 178.4861
},
"rightArm": {
"x": -4.8056,
"y": 9.1559,
"z": -2.5937,
"pitch": 41.2104,
"yaw": 11.1599,
"roll": 11.5835,
"bend": -28.9106,
"axis": -6.4245
},
"leftArm": {
"x": 5.1932,
"y": 9.0568,
"z": -2.5457,
"pitch": 41.2134,
"yaw": -10.0744,
"roll": -11.3678,
"bend": -34.2847,
"axis": 3.7104
},
"rightLeg": {
"x": -1.6738,
"y": 16.311,
"z": 4.4622,
"pitch": -81.0086,
"yaw": 25.937,
"roll": 11.6147,
"bend": -106.5341,
"axis": 178.6748
},
"leftLeg": {
"x": 2.1262,
"y": 16.311,
"z": 4.4622,
"pitch": -80.1541,
"yaw": -23.1741,
"roll": -15.9396,
"bend": -109.5512,
"axis": 186.6826
}
}
]
}
}
@@ -0,0 +1,353 @@
{
"version": 3,
"name": "smooth wave",
"description": "my first animation",
"author": "operatordoge739",
"emote": {
"beginTick": 0,
"endTick": 80,
"stopTick": 81,
"isLoop": false,
"returnTick": 1,
"easeBeforeKeyframe": false,
"degrees": false,
"moves": [
{
"tick": 8,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": -0.040079,
"yaw": -0.062839,
"roll": 0.013404
}
},
{
"tick": 16,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.079561,
"yaw": 0.00633,
"roll": 0.104675
},
"body": {
"pitch": 0.015338,
"yaw": -0.101444,
"roll": 0.071169
},
"rightArm": {
"x": -5.642361,
"y": 3.445788,
"z": -0.219398,
"pitch": -2.739643,
"yaw": 1.23244,
"roll": 0.274965
},
"leftArm": {
"x": 5.348845,
"y": 2.06144,
"z": 0.456518,
"pitch": -0.071698,
"yaw": -0.032049,
"roll": -0.245309
},
"rightLeg": {
"x": -2.024828,
"y": 12.367789,
"z": 0.062098,
"pitch": 0.005119,
"yaw": 0.188187,
"roll": 0.013892
},
"leftLeg": {
"pitch": -0.015376,
"yaw": -0.018557,
"roll": -0.147605
}
},
{
"tick": 23,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": 0.015075,
"yaw": -0.096037,
"roll": 0.011298
},
"rightArm": {
"x": -5.569975,
"y": 3.783614,
"z": -0.392365,
"pitch": -2.998378,
"yaw": 0.910442,
"roll": -0.644675
},
"rightLeg": {
"x": -2.022849,
"y": 12.008524,
"z": 0.109452,
"pitch": 0.007948,
"yaw": 0.194292,
"roll": 0.061405
},
"leftLeg": {
"x": 1.828612,
"y": 11.783937,
"z": -0.067588
}
},
{
"tick": 25,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.03656,
"yaw": -0.056306,
"roll": -0.010938
}
},
{
"tick": 26,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.232569,
"y": 2.037472,
"z": 0.40348,
"pitch": -0.120718,
"yaw": -0.007399,
"roll": -0.1239
}
},
{
"tick": 30,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": 0.019292,
"yaw": -0.103782,
"roll": 0.055881
},
"rightArm": {
"pitch": -2.902291,
"yaw": 1.150272,
"roll": -0.061632
}
},
{
"tick": 31,
"easing": "EASEINOUTQUAD",
"rightLeg": {
"x": -1.991897,
"y": 12.157447,
"z": 0.059089,
"pitch": 0.005119,
"yaw": 0.188187,
"roll": 0.013892
},
"leftLeg": {
"pitch": -0.015376,
"yaw": -0.018557,
"roll": -0.147605
}
},
{
"tick": 32,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.075163,
"yaw": -0.055531,
"roll": -0.039248
}
},
{
"tick": 36,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.232738,
"y": 2.536962,
"z": 0.337457,
"pitch": -0.123332,
"yaw": -0.017768,
"roll": -0.198156
}
},
{
"tick": 37,
"easing": "EASEINOUTQUAD",
"rightArm": {
"pitch": -2.802795,
"yaw": 1.020531,
"roll": -0.376307
}
},
{
"tick": 39,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": 0.01081,
"yaw": -0.089193,
"roll": 0.004038
}
},
{
"tick": 41,
"easing": "EASEINOUTQUAD",
"head": {
"pitch": 0.10722,
"yaw": -0.023033,
"roll": 0.072739
}
},
{
"tick": 44,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"x": 2.093371,
"y": 12.180333,
"z": -0.403448,
"pitch": 0.017084,
"yaw": -0.010917,
"roll": -0.023916
}
},
{
"tick": 46,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.094209,
"y": 2.963737,
"z": 0.290066,
"pitch": -0.130177,
"yaw": 0.00407,
"roll": -0.014502
}
},
{
"tick": 49,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": -0.035429,
"yaw": -0.084483,
"roll": 0.015859
}
},
{
"tick": 56,
"easing": "EASEINOUTQUAD",
"head": {
"x": 0.0,
"y": 0.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
},
{
"tick": 57,
"easing": "EASEINOUTQUAD",
"body": {
"pitch": 0.015473,
"yaw": -0.095783,
"roll": 0.000252
}
},
{
"tick": 59,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.211452,
"y": 2.287216,
"z": 0.189115,
"pitch": 0.007903,
"yaw": -0.010692,
"roll": -0.024953
}
},
{
"tick": 60,
"easing": "EASEINOUTQUAD",
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.093257,
"yaw": 0.058088,
"roll": 0.082813
}
},
{
"tick": 68,
"easing": "EASEINOUTQUAD",
"rightArm": {
"x": -5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
},
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.000513,
"yaw": 0.10914,
"roll": -0.004835
},
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.000177,
"yaw": -0.167171,
"roll": -0.002612
}
},
{
"tick": 72,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.053822,
"yaw": -0.009281,
"roll": -0.012456
}
},
{
"tick": 77,
"easing": "EASEINOUTQUAD",
"leftLeg": {
"x": 1.9,
"y": 12.0,
"z": -0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
},
{
"tick": 78,
"easing": "EASEINOUTQUAD",
"leftArm": {
"x": 5.0,
"y": 2.0,
"z": 0.0,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
},
{
"tick": 80,
"easing": "EASEINOUTQUAD",
"rightLeg": {
"x": -1.9,
"y": 12.0,
"z": 0.1,
"pitch": 0.0,
"yaw": 0.0,
"roll": 0.0
}
}
]
}
}