package org.saturnclient.ui.components; import java.util.function.BooleanSupplier; import org.saturnclient.common.ref.asset.IdentifierRef; import org.saturnclient.config.Theme; import org.saturnclient.ui.Element; import org.saturnclient.ui.ElementContext; import org.saturnclient.ui.RenderScope; import org.saturnclient.ui.resources.Fonts; import org.saturnclient.ui.resources.Textures; /** * One cosmetic in the cosmetics grid: its preview and name. It shows whether * it's equipped, and cosmetics not owned yet are dimmed with a "Get" label. */ public class CosmeticTile extends Element { public static final int HEIGHT = 168; private static final int INSET = 8; private static final int IMAGE_AREA = 124; private static final float NAME_SCALE = 0.62f; private static final float BADGE_SCALE = 0.54f; private static final int BADGE_HEIGHT = 20; /** How long a claim may take before it can be tried again, in ms */ private static final long CLAIM_TIMEOUT = 5000; private final String name; private final IdentifierRef preview; private final float previewAspect; private final BooleanSupplier owned; private final BooleanSupplier equipped; private final Runnable onEquip; private final Runnable onClaim; private long claimedAt = 0; /** * @param previewAspect the preview texture's width divided by its height */ public CosmeticTile(String name, IdentifierRef preview, float previewAspect, int width, BooleanSupplier owned, BooleanSupplier equipped, Runnable onEquip, Runnable onClaim) { this.name = name; this.preview = preview; this.previewAspect = previewAspect; this.owned = owned; this.equipped = equipped; this.onEquip = onEquip; this.onClaim = onClaim; this.width = width; this.height = HEIGHT; } @Override public void render(RenderScope renderScope, ElementContext ctx) { boolean isOwned = owned.getAsBoolean(); boolean isEquipped = equipped.getAsBoolean(); boolean hovered = ctx.isHovering(); int border = isEquipped ? Theme.ACCENT.value : Theme.withAlpha(hovered ? 0.22f : 0.06f, Theme.FOREGROUND.value); renderScope.drawRoundedRectangle(0, 0, width, height, Theme.WIDGET_RADIUS.value, isEquipped ? 2 : 1, Theme.withAlpha(0.72f, Theme.PRIMARY.value), border); // Preview, fitted into its area int areaWidth = width - INSET * 2; renderScope.drawRoundedRectangle(INSET, INSET, areaWidth, IMAGE_AREA, Theme.WIDGET_RADIUS.value - 2, Theme.withAlpha(0.05f, Theme.FOREGROUND.value)); int imageHeight = IMAGE_AREA - 12; int imageWidth = (int) (imageHeight * previewAspect); if (imageWidth > areaWidth - 12) { imageWidth = areaWidth - 12; imageHeight = (int) (imageWidth / previewAspect); } renderScope.drawTexture(preview, INSET + (areaWidth - imageWidth) / 2, INSET + (IMAGE_AREA - imageHeight) / 2, 0, 0, imageWidth, imageHeight, isOwned ? 0xFFFFFFFF : Theme.withAlpha(0.35f, 0xFFFFFFFF)); // Name int nameY = INSET + IMAGE_AREA + (HEIGHT - INSET - IMAGE_AREA - (int) (Fonts.getHeight() * NAME_SCALE)) / 2; renderScope.drawText(NAME_SCALE, Fonts.ellipsize(name, Theme.FONT.value, NAME_SCALE, width - 24), 12, nameY, Theme.FONT.value, isOwned ? Theme.FOREGROUND.value : Theme.getMutedFg()); // Badge in the preview's corner if (isEquipped) { drawBadge(renderScope, "On", Theme.ACCENT.value, Theme.ACCENT_FG.value, true); } else if (!isOwned) { drawBadge(renderScope, isClaiming() ? "Claiming" : "Get", hovered ? Theme.ACCENT.value : Theme.PRIMARY.value, hovered ? Theme.ACCENT_FG.value : Theme.PRIMARY_FG.value, false); } } private void drawBadge(RenderScope renderScope, String text, int background, int foreground, boolean check) { int icon = check ? 10 : 0; int badgeWidth = 14 + icon + (check ? 4 : 0) + (int) (Fonts.getWidth(text, Theme.FONT.value) * BADGE_SCALE); int x = width - INSET - 6 - badgeWidth; int y = INSET + 6; renderScope.drawRoundedRectangle(x, y, badgeWidth, BADGE_HEIGHT, 6, background); if (check) { renderScope.drawTexture(Textures.ICON_CHECK, x + 7, y + (BADGE_HEIGHT - icon) / 2, 0, 0, icon, icon, foreground); } int textY = y + (BADGE_HEIGHT - (int) (Fonts.getHeight() * BADGE_SCALE)) / 2; renderScope.drawText(BADGE_SCALE, text, x + 7 + icon + (check ? 4 : 0), textY, Theme.FONT.value, foreground); } @Override public void click(int mouseX, int mouseY) { if (owned.getAsBoolean()) { if (!equipped.getAsBoolean()) { onEquip.run(); } } else if (!isClaiming()) { claimedAt = System.currentTimeMillis(); onClaim.run(); } } private boolean isClaiming() { return System.currentTimeMillis() - claimedAt < CLAIM_TIMEOUT; } }